はじめに
Javaではすべての入出力をストリームと呼ばれる仕組みをつかって実装します。データを細い流れに見立て順番に吸い上げたり、送り出したりすることからそのように呼ばれています。ストリームを利用することで、入出力の媒体に依存することなく同じ要領で操作することができます。
Streamの分類
ストリームは、特定の媒体からデータを読み込むための入力ストリーム(InputStream)と、データを順番に媒体に書き出すための出力ストリーム(OutputStream)に分類できます。java.io パッケージでは、これらの抽象基底クラスをもとにファイルやバッファ、配列などを操作する様々な実装クラスを提供しています。
テキストファイルの読み書き
ストリーム実装クラスのFileReader/FileWriterクラスを使ってテキストファイルの読み書きをプログラム上で行ってみましょう。
FileReader/FileWriterクラスはテキストファイルを読み書きするための入出力ストリームを提供します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | FileReader reader = null; FileWriter writer = null; try { //読み書きファイルをオープン reader = new FileReader(new File("C:/Data/input.txt")); writer = new FileWriter(new File("C:/Data/input.txt")); //順次読み込み&転記 int data; while ((data = reader.read()) != -1) { writer.write(data); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { //クローズ処理 try { if (reader != null) { reader.close(); } if (writer != null) { writer.close(); } } catch (IOException e) { e.printStackTrace(); } } |
文字列を読み込むのはreadメソッドの役割です。readメソッドはファイルから次の1文字を読み込み、その文字をint型で表したものを返すメソッドです。ストリームの終わりに達した場合は -1 を返します。この性質を利用してreadメソッドが -1 をかえすまでwhileループを回し、ファイルの内容を読み込みます。読み込んだ内容は、FileWriterクラスのwriteメソッドでファイルに転記します。
ストリームによる処理は必ずtryブロックでくくり、クローズ処理はfinallyブロックで表すのが基本です。正しくクローズされなかったリソースはメモリを圧迫する原因となるので必ずクローズを行いましょう。
バイナリファイルの読み書き
FileInputStream/FileOutputStreamクラスを利用して、画像などのバイナリファイルを読み書きするための入出力ストリームを使用できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | try { //読み書きファイルをオープン reader = new FileInputStream(new File("C:/image/input.gif")); writer = new FileOutputStream(new File("C:/Data/input.gif")); //順次読み込み&転機 int data; while ((data = reader.read()) != -1) { writer.write(data); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { //クローズ処理 try { if (reader != null) { reader.close(); } if (writer != null) { writer.close(); } } catch (IOException e) { e.printStackTrace(); } } |
上記はinput.gifの内容をFileInputStreamで読み込み、その結果をFileOutputStreamでoutput.gifに出力するサンプルです。
コンストラクター、read/write メソッドの構文はFileReader/FileWriterクラスと同様です。
読み書きのバッファリング処理
ファイルやネットワークに対する読み書きは、メモリに対するものと比べると、速度が格段に遅くなりがちです。そうした速度の遅くなる媒体に対して、1文字、1バイト単位でのアクセスは効率が良くありません。バッファリング処理を使うことでこの問題を解決しましょう。バッファーとは、文字列などのデータを一時的に保存するためのメモリ上の領域のことをいいます。バッファリングを利用すると、データをバッファー領域に蓄積し、いっぱいになったところでまとめてファイルを出力したり、読み込んだりします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | BufferedReader reader = null; BufferedWriter writer = null; try { //読み書きファイルをオープン reader = new BufferedReader( new InputStreamReader( new FileInputStream("C:/Data/input.dat"), "UTF-8")); writer = new BufferedWriter( new OutputStreamWriter( new FileOutputStream("C:/Data/output.dat"), "UTF-8")); //順次読み込み&転機 int data; while ((data = reader.read()) != -1) { writer.write(data); } writer.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { //クローズ処理 try { if (reader != null) { reader.close(); } if (writer != null) { writer.close(); } } catch (IOException e) { e.printStackTrace(); } } |
バッファリング処理を行うには、BufferedReader/BufferedWriterコンストラクターでInputStreamReader/OutputStreamWriterを受け取るだけです。
flushメソッドで、バッファ内容を出力ストリームに出力します。バッファリング処理を利用している時は、writeメソッドの呼び出しによって即座に出力が開始されるかどうかはわかりません。
ネットワーク通信などで、出力のタイミングを厳密に制御する必要がある場合は、flushメソッドを明示的に呼び出す必要があります。
さいごに
今回は、Javaのストリームについてまとめてみました。通信を伴った実装を行う時には必須のクラスであるので、基本的な部分をまとめることにしました。