Reader に文字コード指定できない件で、標準ライブラリに無ければ commons を使えばいいじゃない、という発言を見かけたので、commons.io を覗いてみた。FileUtils クラスに
FileUtils#readLines(File,String) FileUtils#lineIterator(File,String)
といった static メソッドがあるようだ。これの事を言っていたのだろうか?
LineIterator はその名の通り Iterator インターフェースを実装しているのだが、next() メソッドでストリーム読み込みに付き物の IOException をどう処理しているのか気になる。ソースを見ると、next() メソッド内部で IOException が発生した場合は、IllegalStateException をスローするようになっている。next() では検査例外をスローできないので仕方ないとはいえ、非検査例外を投げるというのはアリなのか?
しかし、それで問題ないのならば、拡張for文が使える今となっては Iterator を返すユーティリティメソッドよりも、ストレートに Iterable を実装した Reader クラスの方が嬉しい。というわけで書いてみた。
public class LineReader extends Reader implements Iterable<String> { private BufferedReader reader; private IOException ioException; public LineReader(BufferedReader reader) { super(); this.reader = reader; } public LineReader(Reader reader) { this(new BufferedReader(reader)); } private class LineIterator implements Iterator<String> { private String line; private String nextLine() throws IOException { String line = this.line; if (line == null) { line = reader.readLine(); if (line == null) { throw new NoSuchElementException(); } } else { this.line = null; } return line; } private boolean hasNextLine() throws IOException { if (this.line == null) { this.line = reader.readLine(); } return this.line != null; } public boolean hasNext() { try { return this.hasNextLine(); } catch (IOException e) { ioException = e; return false; } } public String next() { try { return this.nextLine(); } catch (IOException e) { ioException = e; throw new IllegalStateException(e); } } public void remove() { throw new UnsupportedOperationException(); } } public boolean hasError() { return this.ioException != null; } public void raiseError() throws IOException { if (this.ioException != null) { throw ioException; } } @Override public void close() throws IOException { this.reader.close(); } @Override public int read(char[] cbuf, int off, int len) throws IOException { return reader.read(cbuf, off, len); } public Iterator<String> iterator() { return new LineIterator(); } }
前回作ったMultibyteTextReaderを使って
LineReader lr = new LineReader(new MultibyteTextReader("hoge.txt", "utf-8")); try { for(String line: lr) { System.out.println(line); } } finally { r.close(); }
みたいにスッキリ書ける。適当に作ったけど意外とよさげな感じ。
これなら冗長と揶揄にされるJavaのファイル読み込みも、かなーり簡単に書けるようになるんじゃないか。もっと短くしたければファイル名とエンコーディングを引数とするLineReaderコンストラクタを作るといいかもな。