【问题标题】:Files.readAllBytes vs Files.lines getting MalformedInputExceptionFiles.readAllBytes vs Files.lines 得到 MalformedInputException
【发布时间】:2015-07-08 09:02:48
【问题描述】:

我原以为以下两种读取文件的方法应该表现相同。但他们没有。第二种方法是抛出MalformedInputException

public static void main(String[] args) {    
    try {
        String content = new String(Files.readAllBytes(Paths.get("_template.txt")));
        System.out.println(content);
    } catch (IOException e) {
        e.printStackTrace();
    }

    try(Stream<String> lines = Files.lines(Paths.get("_template.txt"))) {
        lines.forEach(System.out::println);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

这是堆栈跟踪:

Exception in thread "main" java.io.UncheckedIOException: java.nio.charset.MalformedInputException: Input length = 1
    at java.io.BufferedReader$1.hasNext(BufferedReader.java:574)
    at java.util.Iterator.forEachRemaining(Iterator.java:115)
    at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
    at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
    at Test.main(Test.java:19)
Caused by: java.nio.charset.MalformedInputException: Input length = 1
    at java.nio.charset.CoderResult.throwException(CoderResult.java:281)
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:339)
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
    at java.io.InputStreamReader.read(InputStreamReader.java:184)
    at java.io.BufferedReader.fill(BufferedReader.java:161)
    at java.io.BufferedReader.readLine(BufferedReader.java:324)
    at java.io.BufferedReader.readLine(BufferedReader.java:389)
    at java.io.BufferedReader$1.hasNext(BufferedReader.java:571)
    ... 4 more

这里有什么区别,我该如何解决?

【问题讨论】:

  • 一种方法读取字节,一种读取文本。你的文件是文本文件吗?什么是编码?
  • 此外,Files.lines() 的默认编码是 UTF-8。并非每个字节序列都是有效的 UTF-8。
  • 嗯,我想我需要一点帮助来回答你的问题。该文件是一个文本文件。用记事本++打开它在状态栏中显示ANSI。这能回答你的问题吗?
  • @Phylogenesis 感谢您的链接。前段时间我确实读过。我最近决定再读一遍。可能现在是阅读它的正确时机:-D

标签: java file-io stream


【解决方案1】:

这与character encoding 有关。计算机只处理数字。要存储文本,必须使用某种方案将文本中的字符转换为数字和数字。该方案称为字符编码。有许多不同的字符编码;一些众所周知的标准字符编码是 ASCII、ISO-8859-1 和 UTF-8。

在第一个示例中,您读取文件中的所有字节(数字),然后通过将它们传递给类String 的构造函数将它们转换为字符。这将使用您系统的默认字符编码(无论您的操作系统是什么)将字节转换为字符。

在第二个示例中,您使用Files.lines(...),根据the documentation,将使用UTF-8 字符编码。当在文件中发现不是有效 UTF-8 序列的字节序列时,您将获得 MalformedInputException

您系统的默认字符编码可能是也可能不是 UTF-8,因此可以解释行为上的差异。

您必须找出文件使用的字符编码,然后明确使用它。例如:

String content = new String(Files.readAllBytes(Paths.get("_template.txt")),
        StandardCharsets.ISO_8859_1);

第二个例子:

Stream<String> lines = Files.lines(Paths.get("_template.txt"),
        StandardCharsets.ISO_8859_1);

【讨论】:

  • Interesting... 这意味着Files.lines() 中使用的CharsetDecoder 默认使用CodingErrorAction.REPORT。这没有记录,并且与 JDK 提供的任何默认 Reader 不同...
  • @fge Files.lines() 从您传递的Charset 创建一个CharsetDecoder,并且CharsetDecoderAPI docs 说:错误输入的默认操作和unmappable-character 错误是报告它们。
  • 嗯,这个默认操作在 JDK 中几乎无处不在 :)
  • @fge 我猜旧的Reader 类会以不同的方式实现向后兼容性。 (也很奇怪Files.lines() 默认使用 UTF-8 而大多数其他 I/O 类默认使用系统的默认字符集)。标准类在很多方面都不是很一致...
【解决方案2】:

为了补充Jesper's answer,这里发生的事情(并且没有记录!)是Files.lines() 创建了一个CharsetDecoder,其策略是拒绝无效的字节序列;也就是说,它的CodingErrorAction 设置为REPORT

这与 JDK 提供的几乎所有其他 Reader 实现所发生的情况不同,JDK 的标准策略是 REPLACE。此策略将导致所有不可映射的字节序列发出replacement character (U+FFFD)

【讨论】:

    【解决方案3】:

    Files.lines 默认使用UTF-8 encoding,而从字节实例化新字符串将使用默认系统编码。您的文件似乎不是 UTF-8,这就是它失败的原因。

    检查您的文件使用的编码,并将其作为第二个参数传递。

    【讨论】:

      【解决方案4】:

      2017 年使用:

       Charset.forName("ISO_8859_1") instead of Charsets.ISO_8859_1
      

      【讨论】:

      • 你能补充更多细节吗?
      • @Angelo.Hannes 像这样使用它: Stream stream = Files.lines(Paths.get("/User/me/file.log"), Charset.forName("ISO_8859_1" ) )
      猜你喜欢
      • 2021-08-23
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 2015-04-14
      • 2022-08-05
      • 1970-01-01
      • 2019-10-22
      相关资源
      最近更新 更多