ファイルを読み取るための次の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
ここの違いは何ですか、どうすれば修正できますか?
これは、 文字エンコーディング と関係があります。コンピューターは数字のみを扱います。テキストを保存するには、何らかのスキームを使用して、テキスト内の文字を数字に変換したり、数字から変換したりする必要があります。そのスキームは、文字エンコーディングと呼ばれます。多くの異なる文字エンコーディングがあります。よく知られている標準文字エンコーディングの一部は、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);
Jesper's answer を補完するために、ここで何が起こるか(そして文書化されていません!)は、Files.lines()
が CharsetDecoder
を作成することです。無効なバイトシーケンス。つまり、その CodingErrorAction
はREPORT
に設定されます。
これは、標準ポリシーがReader
であるJDKが提供する他のほぼすべてのREPLACE
実装で起こることとは異なります。このポリシーにより、すべてのマップ不可能なバイトシーケンスで 置換文字(U + FFFD) が出力されます。
Files.lines
はデフォルトで TF-8エンコーディング を使用しますが、バイトから新しい文字列をインスタンス化するにはデフォルトのシステムエンコーディングを使用します。ファイルはUTF-8ではないようであるため、失敗しています。
ファイルが使用しているエンコードを確認し、2番目のパラメーターとして渡します。
2017年の使用:
Charset.forName("ISO_8859_1") instead of Charsets.ISO_8859_1