web-dev-qa-db-ja.com

Files.readAllBytesとFiles.linesでMalformedInputExceptionが発生する

ファイルを読み取るための次の2つのアプローチは、同じように動作するはずだと考えていました。しかし、そうではありません。 2番目のアプローチは、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

ここの違いは何ですか、どうすれば修正できますか?

16
Angelo.Hannes

これは、 文字エンコーディング と関係があります。コンピューターは数字のみを扱います。テキストを保存するには、何らかのスキームを使用して、テキスト内の文字を数字に変換したり、数字から変換したりする必要があります。そのスキームは、文字エンコーディングと呼ばれます。多くの異なる文字エンコーディングがあります。よく知られている標準文字エンコーディングの一部は、ASCII、ISO-8859-1、およびUTF-8です。

最初の例では、ファイル内のすべてのバイト(数値)を読み取り、それらをクラスStringのコンストラクターに渡すことで文字に変換します。これは、システムのデフォルトの文字エンコーディング(オペレーティングシステムの種類にかかわらず)を使用して、バイトを文字に変換します。

Files.lines(...)を使用する2番目の例では、 ドキュメント に従って、UTF-8文字エンコードが使用されます。有効なUTF-8シーケンスではないバイトシーケンスがファイル内で見つかると、MalformedInputExceptionを取得します。

システムのデフォルトの文字エンコーディングはUTF-8である場合とそうでない場合があるため、動作の違いを説明できます。

ファイルに使用されている文字エンコードを見つけてから、明示的に使用する必要があります。例えば:

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

2番目の例:

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

Jesper's answer を補完するために、ここで何が起こるか(そして文書化されていません!)は、Files.lines()CharsetDecoder を作成することです。無効なバイトシーケンス。つまり、その CodingErrorActionREPORTに設定されます。

これは、標準ポリシーがReaderであるJDKが提供する他のほぼすべてのREPLACE実装で起こることとは異なります。このポリシーにより、すべてのマップ不可能なバイトシーケンスで 置換文字(U + FFFD) が出力されます。

6
fge

Files.linesはデフォルトで TF-8エンコーディング を使用しますが、バイトから新しい文字列をインスタンス化するにはデフォルトのシステムエンコーディングを使用します。ファイルはUTF-8ではないようであるため、失敗しています。

ファイルが使用しているエンコードを確認し、2番目のパラメーターとして渡します。

1
Evan Knowles

2017年の使用:

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