当尝试读取整个文件时出现了MalformedInputException异常

8

我有一个132 kb的文件(你不能说它很大),我试图在Scala REPL中读取它,但是我无法读取超过2048个字符,因为它会给我一个java.nio.charset.MalformedInputException异常。

以下是我采取的步骤:

val it = scala.io.Source.fromFile("docs/categorizer/usig_calles.json") // this is ok
it.take(2048).mkString // this is ok too
it.take(1).mkString // BANG!

java.nio.charset.MalformedInputException: Input length = 1
at java.nio.charset.CoderResult.throwException(CoderResult.java:277)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:338)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
at java.io.InputStreamReader.read(InputStreamReader.java:184)

你有任何想法出了什么问题吗?

--

显然问题在于文件不是UTF编码

我将其保存为UTF,一切都正常,我只需对迭代器发出mkString指令,就可以检索文件的全部内容

奇怪的是,错误只在传递前2048个字符时出现...

3个回答

5

没有文件很难确定,但异常文档表明它在“给定字符集不合法的输入字节序列或输入字符序列不是合法的十六位Unicode序列”时抛出。(MalformedInputException javadoc

我怀疑2049是遇到的第一个无效字符,使用默认的JVM字符编码。考虑使用fromFile的重载之一显式声明文件的字符编码。

如果应用程序将跨平台使用,则应知道JVM上的默认字符编码因平台而异,因此如果您使用特定编码,则需要在启动应用程序时将其作为命令行参数明确设置,或者在每个调用中使用适当的重载指定它。


2
有一种简单的方法来测试这个问题:第一次尝试取2049个元素。然而,这很可能不是真正的问题——在文件中恰好在2 ^ 11 + 1个字符处遇到第一个非法字节序列只是一个巧合。 - Travis Brown
1
这是我尝试的第一件事,对整个迭代器进行了一个简单的mkString。然后我追踪到了2048... - opensas

4
如果您只想将字节转换为普通拉丁数据:
// File:
io.Source.fromFile(file)(io.Codec.ISO8859).mkString

// InputStream:
io.Source.fromInputStream(System.io)(io.Codec.ISO8859).mkString

4
任何时候,如果您在同一个迭代器上两次调用 take,那么就没有任何保证了。迭代器本质上是命令式的,并将其与函数式习惯用法混合使用最多会产生不确定因素。你在标准库中遇到的大多数迭代器在这方面都表现得相当良好,但一旦你使用了 takedrop 或者 filter 等等,你就处于未定义行为区域,原则上任何事情都有可能发生。
文档中可以看到:

特别需要注意的是,除非另有说明,否则绝不能在调用迭代器方法后再使用迭代器。最重要的两个例外也是唯一的抽象方法:nexthasNext...

def take(n: Int): Iterator[A] ...

重用:在调用此方法后,应该丢弃它被调用的迭代器,并仅使用返回的迭代器。使用旧迭代器是未定义的,可能会改变新迭代器。

所以,也许没有必要追查到底出了什么问题。

我建议从一开始就使用 it.toList。这样,他将能够拥有所有的数据。 - pedrofurla
@pedrofurla:没错,或者如果他(或她?企鹅头像图片被谨慎地裁剪了,所以我无法确定)不一定需要读取整个文件,则可以使用toStream - Travis Brown

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接