テキストファイルへの書き込みを理解したところで、CSV(Comma Separated Value)、TDV(Tab Delimited Value)形式のファイルのように、定型的なフォーマットを持つテキストファイルをプログラム中から読み込む方法について見ていくことにしましょう。
先にも述べたように、データストアとしての選択肢はRDBばかりではありません。少量のデータをシーケンシャルに処理したい、また、RDBが利用できない限定された環境においては、簡易なデータストアとしてのテキストファイルを積極的に検討してみてはいかがでしょうか。
リスト3はファイルを読み込み、その内容を表示する一例です。
<%@ page contentType="text/html;charset=Shift_JIS" import="java.io.*,java.util.*" %> <table border="1"> <tr> <th>アクセス日時</th><th>URL</th><th>IPAddress</th> <th>リンク元</th><th>ユーザエージェント</th> </tr> <% FileReader objFr=new FileReader(application.getRealPath("sample.log")); BufferedReader objBr=new BufferedReader(objFr); String line = ""; while((line = objBr.readLine()) != null){ StringTokenizer objTkn=new StringTokenizer(line,"\t"); out.println("<tr>"); while(objTkn.hasMoreTokens()){ out.println("<td nowrap>" + objTkn.nextToken() + "</td>"); } out.println("</tr>"); } objBr.close(); %> </table>
読者から上記のサンプルコード、「while (objBr.ready())」について貴重なご指摘をいただき、コードを差し替え、また編集部注を追記させて頂きました。
内容について正確を期せずに混乱を招いた点、読者の皆様におわび申し上げます。
リスト3の実行結果は以下のとおりです。
リスト3の内容を説明しましょう。Javaでファイル読み込み用の機能を提供するのは、FileReaderクラスの役割です。FileReaderクラスはそれ単体でも読み込み可能ですが、FileWriterクラス同様の理由で、一般的にBufferedReaderクラスと併せて使用するのが通例です。
BufferedReaderクラスは、テキストファイルの読み込みに際して、現在の読み込み位置を表す「ファイルポインタ」を内部的に保持しています。ファイルポインタはテキストファイルを開いた直後にはファイルの先頭にあり、あとは読み込み処理の進行に従って後方に移動していきます。
BufferedReader#readyメソッドは、このファイルポインタが現在「読み込み処理可能な位置にあるかどうか」をtrue/falseで返しますが、ここではこの性質を利用して、readyメソッドがtrueの間——つまり、ポインタがファイルの終端に到達するまで読み取り処理を繰り返すことで、ファイルをシーケンシャルに読み込んでいるというわけです。
編集部注:BufferedReader#readyメソッドは、読み込み行が残っている場合でもデータがバッファに貯まっていないと、falseの判定を返すことがあります(ソケットの場合など)。APIリファレンスでも「
戻り値:次の read() が入力をブロックしないことが確実な場合は true、そうでない場合は false。false が返されても、次の読み込みが確実にブロックするというわけでない
」と記載があります。そのため、ファイルの内容をすべて読み込んだか判定するサンプルコード「while (objBr.ready())」の部分を「while((line = objBr.readLine()) != null)」と判定するコードに置き換えていますので、ご注意ください。
whileループの中では、BufferedReader#readLineメソッドでテキストファイルの中身を1行単位に読み込んでいますが、これをタブ文字(\t)で分割するのがStringTokenizerクラスの役割です。
StringTokenizer objTkn=new StringTokenizer(objBr.readLine(),"\t");
StringTokenizerクラスは、第1引数に分割したい文字列を、第2引数に区切り文字を指定します。ここではタブ文字を表わす「\t」を指定していますが、もしもCSV形式のファイルを処理したい場合には「,」に置き換えるだけです。
その分割された文字列を内側のwhileループで処理しています。hasMoreTokensメソッドは、次の文字列トークン(分割された部分文字列)が存在するかどうかをtrue/falseで返します。つまりここでは、hasMoreTokensメソッドがfalseを返すまで(つまり、文字列トークンがなくなるまで)ループを繰り返し、文字列トークンを順番に取得しているというわけです(nextTokenメソッド)。
ちなみに、J2SE1.4からは、String#splitメソッドが用意されており、StringTokenizerクラスを代替することも可能です。
String[] aryCol=objBr.readLine().split("\t"); for(int i=0;i<sryCol.length;i++){ out.println("<td nowrap>" + aryCol[i] + "</td>"); }
テキストファイルへの読み込みには、FileReade/BufferedReaderクラスを併用するのが一般的です。BufferedReaderクラスが内部的に保持するファイルポインタを利用することで、テキストファイルをシーケンシャルに処理することができます。
Copyright © ITmedia, Inc. All Rights Reserved.