Files.readAllBytes vs Files.lines getting MalformedInputException

2022-09-02 11:56:28

我本来以为以下两种读取文件的方法应该表现一致。但事实并非如此。第二种方法是抛出一个 .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

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


答案 1

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

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

在第二个示例中,使用 ,根据文档,将使用 UTF-8 字符编码。当在文件中发现不是有效 UTF-8 序列的字节序列时,您将获得 .Files.lines(...)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);

答案 2

为了补充Jesper的答案,这里发生的事情(并且没有记录!)是创建一个CharsetDecoder,其策略是拒绝无效的字节序列;也就是说,其编码错误操作设置为 。Files.lines()REPORT

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