読み込み処理を作る
続いて、ファイルからテキストを読み込む処理を作成しましょう。これも、まずは最もシンプルな(例外処理をしていない)形で全体の流れをつかみます。
public static void main(String[] args) { FileReader reader = new FileReader("data.txt"); BufferedReader breader = new BufferedReader(reader); String s = null; String loaded = ""; while((s = breader.readLine()) != null){ loaded += s.trim() + "\r\n"; } breader.close(); System.out.println(loaded); }
これは、「data.txt」というテキストファイルを読み込んでそのテキストを出力する、最も基本となる処理です。ただし、例外処理などを一切していませんから、このままでは動きません。
ざっと流れを見ましょう。テキストファイルの読み込みに利用するクラスは「FileReader」というものになります。また、例によって、これにバッファ機能を付加するためのものとして「BufferedReader」というクラスも用意されています。
FileReader reader = new FileReader("data.txt"); BufferedReader breader = new BufferedReader(reader);
これらのインスタンス生成は、FileWriter/BufferedWriterとほぼ同じです。まず、読み込むテキストファイル名を引数に指定してFileReaderインスタンスを作成し、続いてこのインスタンスを引数にしてBufferedReaderを作成します。これで、指定のファイルからテキストを読み込む、バッファ機能つきのインスタンスが用意されます。
読み込みの注意ポイント
後は、メソッドを呼び出してテキストを読み込むだけです。
しかし、書き出しと読み込みが最も異なるのは「対象となるテキストの量があらかじめわかっていない」という点にあります。書き出しは、既に用意してあるテキストをただ書くだけです。ですが、読み込みの場合は、そのファイルにどれだけテキストが保存されているかはわかりません。ほんの数文字かもしれないし、数千KBもの巨大なテキストファイルなのかもしれません。
そこで、通常は「1行だけテキストを読み込む」というメソッドを繰り返し呼び出していき、ファイルの終わりまできたら繰り返しを抜ける、というやり方をします。
while((s = breader.readLine()) != null){ loaded += s.trim() + "\r\n"; }
この部分です。ここでは、BufferedReaderの「readLine」というメソッドを使って読み込みを行っています。これは名前の通り、改行コードまでテキストを読み込んで返すものです。これはメソッドを呼び出すと次々テキストを行単位(正確には段落単位)で読み込んでいきます。これ以上テキストがない(つまりファイルの末尾まで読み込んだ)場合には、nullが返されます。
従って、繰り返しでreadLineを呼び出し、これがnullでない間、繰り返しを続けるようにしておけば、ファイルの末尾までのテキストをすべて読み込むことができます。ここでのwhileの条件を見ると、((s = breader.readLine()) != null)というようになっています。この(s = breader.readLine())は、テキストが読み込まれればそのテキスト、なければnullの値になりますから、これがnullかどうかをチェックすればよいのです。
さて、読み込んだテキストは、繰り返し部分で1つの変数に収めています。ここでは「trim」というメソッドが使われていますが、これは、テキストの先頭と末尾にある「ゴミ」を取り除くものです。ゴミとは、特殊な制御用コード(改行コードなど)とかスペースなどです。こうして余計な文字を取り除いたものを、「\r\n」をつけてテキストにつなげています(この「\r\n」は、Windowsで一般的に用いられている改行コードを示す記号です)。
テキストの読み込みは、このように、
- 少しずつテキストを読み込んでいく
- ファイルの最後まで読み込んだかをチェックする
- 読み込んだテキストの前後にある空白を取り除くなどの処置をする
といった、書き出しにはなかった注意すべき点があります。
読み込みに例外処理を組み込む
では、基本的な流れがわかったところで、これに例外処理を組み込んで完成させることにしましょう。
public static void main(String[] args) { String loaded = ""; BufferedReader breader = null; try { FileReader reader = new FileReader("data.txt"); breader = new BufferedReader(reader); String s = null; loaded = ""; while((s = breader.readLine()) != null){ loaded += s.trim() + "\r\n"; } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { breader.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println(loaded); }
ここでも、読み込み処理を行っている部分全体をtry内に入れて実行させるようにしてあります。読み込みの場合、発生する例外には2種類のものがあります。1つは、ファイルアクセスに関するIOExceptionですが、もう1つは「開こうとしたファイルがない」場合に発生するFileNotFoundExceptionです。
そこで、ここでは2つのcatchを用意して、それぞれで例外をとらえるようにしてあります。try {}に続く部分を見てみると、
……
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
……
となっているのが分かります。このように、catch(){……}catch(){……}というようにしてcatch節を続けて記述することで、個々の例外に対応した処理を用意できるわけです。ただ、今回はどちらもprintStackTraceしかしてないので、あまり分ける意味はないのですが。
中には「1つ1つの例外クラスにcatchを用意するなんて、そんなの面倒だ!」と思う人もいるかもしれません。そういう場合は、引数に「Exception」クラスを指定したcatchを1つだけ用意すれば事足ります。すべての例外は、このExceptionを継承して作られますから、こうすれば発生する例外はすべてこのcatchで受け止めることができます。
ただし、こうした書き方は、正直、あまりお勧めはできません。プログラムのどこを見ても全部同じ「catch(Exception e)……」としか書いていない、ということは、プログラマが「発生した例外をきちんと見ていない」ということになりませんか? やはり、どんな例外が発生したのか、それに対してどう処理をするのか、プログラマならそうしたことをきちんと考えないといけないでしょう。

