細かいけど。
こう思ってた
遥か昔、Java 1.1 の時代から、人類は InputStreamReader をひとつひとつ丁寧に手作りしてきた。
// パターン A String path = "foo.txt"; Charset charset = Charset.forName("Shift_JIS"); try (BufferedReader br = new BufferedReader( new InputStreamReader(new FileInputStream(path), charset))) { br.lines().forEach(System.out::println); }
Java 7 で java.nio.file.Files クラスが追加されたときに、newBufferedReader(Path, Charset) という static メソッドがあって、「わぁいこれからはみじかくかけるぞ」と思ったものだ。
// パターン B Path path = Paths.get("foo.txt"); Charset charset = Charset.forName("Shift_JIS"); try (BufferedReader br = Files.newBufferedReader(path, charset)) { br.lines().forEach(System.out::println); }
同じクラスに readAllLines(Path, Charset) というのもあって、小さなファイルならさらに短く書けると思った。
// パターン C Path path = Paths.get("foo.txt"); Charset charset = Charset.forName("Shift_JIS"); Files.readAllLines(path, charset).forEach(System.out::println);
違ってた
上のサンプルでは Shift_JIS を指定してたけど、世の中には、
- Windows-31J には含まれてるけど Shift_JIS には含まれていない文字
- シフト JIS 的に正しくないオクテット列
というものがあって、自分で InputStreamReader を作ってファイルを読む場合は、U+FFFD に変換されていた。
その後、Java 1.4 で NIO が導入されて java.nio.charset.CharsetDecoder が追加されたとき、そういう場合にどうするかを選べるようになった。選択肢は java.nio.charset.CodingErrorAction に定数として定義されている。
定数 | ざっくり説明 |
---|---|
REPLACE | U+FFFD に変換する |
IGNORE | 無視する |
REPORT | エラー扱いにする |
InputStreamReader を自分で作る場合は REPLACE にあたる設定になっている。これに対し、Files クラスの newBufferedReader(Path, Charset) や readAllLines(Path, Charset) だと REPORT が使われて、不明な文字やオクテット列があると、例外が throw される。
この差異が問題になるかどうかはユースケースによるけど、大きなトラブルの元になることもあるかもしれない。