10.2 ストリームへの書き込み
10.2.1 出力ストリームの対象
ストリームを制御する多くの入出力関連クラスは、java.io パッケージで宣言されています。入出力を行う場合は java.io パッケージをインポートしておくと便利でしょう。
java.io パッケージは、入力ストリームを表す java.io.InputStream クラス、出力ストリームを表す java.io.OutputStream クラス、読み込みをサポートする java.io.Reader クラス、書き込みをサポートする java.io.Writer クラスの 4 種類に大別することができ、これらから派生して多くのサブクラスが定義されています。java.io パッケージは多くのクラスが宣言されていて複雑な関係を築いていますが、基本的にはこれら 4 つのクラスから構成されています。
データを入出力するだけであれば、ストリームを生成するだけでも可能ですが、ストリームに対する入出力は byte 型の配列として行う必要があります。文字列やその他のオブジェクトを効率よく送受信するためには、Reader や Writer クラスのサブクラスを用いるということになるでしょう。
ストリームにデータを書き込むには、まずOutputStream のインスタンスを生成する必要があります。OutputStream クラスは出力ストリームを表す抽象クラスです。そのストリームが具体的にどのハードウェアを参照するかは、サブクラスが決定します。OutputStream は出力ストリームが提供するべき基本的なメソッドを宣言しています(表1)。
メソッド | 解説 |
---|---|
void close() | このストリームを閉じ、このストリームに関連するすべてのシステムリソースを解放します。 |
void flush() | この出力ストリームをフラッシュします。 |
void write(byte[] b) | 指定されたバイト配列を書き込みます。 |
void write(byte[] b, int off, int len) | オフセット off から始まる指定のバイト配列を len バイトを書き込みます。 |
abstract void write(int b) | 指定されたバイトを書き込みます。 |
write() メソッドを用いれば、バイト配列としてデータを出力することができます。テキストデータも最終的にはバイト配列に変換できるため、基本的にはこのメソッドだけであらゆるデータを出力することができます。
後は、データを書き込む装置を対象とするストリームを選択するだけです。例えば、ファイルにデータを書き込みたい場合は java.io.FileOutputStream クラスのインスタンスを生成します。FileOutputStream クラスは OutputStream クラスを継承しています。
コンストラクタ | 解説 |
---|---|
FileOutputStream(File file) | File オブジェクトで表されるファイルに書き込むためのファイル出力ストリームを作成します。 |
FileOutputStream(File file, boolean append) | File オブジェクトが表すファイルに書き込むための出力ファイルストリームを作成します。 |
FileOutputStream(FileDescriptor fdObj) | ファイル記述子に書き込むための出力ファイルストリームを作成します。 |
FileOutputStream(String name) | name のファイルに書き込むためのファイル出力ストリームを作成します。 |
FileOutputStream(String name, boolean append) | name のファイルに書き込むための出力ファイルストリームを作成します。 |
メソッド | |
protected void finalize() | ファイルへの接続をクリーンアップし、このストリームへの参照がなくなったときにこのファイル出力ストリームの close メソッドが呼び出されるようにします。 |
FileChannel getChannel() | このファイル出力ストリームに関連付けられた、一意の FileChannel オブジェクトを返します。 |
FileDescriptor getFD() | ストリームに関連したファイル記述子を返します。 |
表2は FileOutputStream クラスのコンストラクタとメソッドです。もちろん、OutputStream クラスのメソッドもオーバーライドされているため write() メソッドなどを用いて生成したファイルにデータを書き込むことができます。
import java.io.*; class Test { public static void main(String args[]) { byte data[] = { 0x4B , 0x69 , 0x74 , 0x74 , 0x79 }; try { OutputStream out = new FileOutputStream("test.txt"); out.write(data); out.close(); } catch(Exception err) { System.err.println(err); } } }
>java Test >type test.txt Kitty
コード1は、FileOutputStream クラスを用いて記録ディスク上のファイルに対してストリームを関連付けています。FileOutputStream() コンストラクタは、セキュリティなどの理由からストリームを開けない場合 java.io.FileNotFoundException 例外をスローするため try 文を用いています。ファイルが存在しない場合は新しく生成され、すでに存在する場合は新しい内容で上書きされます。
開いた出力ストリームには、バイト配列を出力することができるため、"Kitty" という ASCII 文字列を表す配列 data をあらかじめ作成し、これを write() メソッドに渡しています。実行結果を見てわかるように、テキストファイルが生成され、文字列が正しく書き込まれています。ストリームを使い終わったら colse() メソッドを用いて解放する事を忘れないで下さい。
OutputStream クラスには flush() メソッドが宣言されています。このメソッドはストリームの一時記憶領域に蓄えられた情報をすべて書き込むことを要求します。実は、write() メソッドなどからデータを出力しても、それがすぐに対象のストリームに書き込まれるわけではないのです。対象が主記憶装置などの高速な転送装置であれば良いのですが、ディスクドライブやネットワークなどの低速な出力対象も存在します。この場合、出力するたびにデータを書き込むと、ディスクの回転待ちなどが発生してプログラムが無駄に待機してしまいます。そこで、出力されたデータは一時的な記録領域に蓄えられるのです。この一時的な記録領域をバッファと呼んでいます。
そして、バッファ内のデータが出力対象の装置に書き込む最良のサイズになった時、初めて書き込まれます。つまり、それまではデータがバッファに記録されるだけで、対象の装置に書き込まれるわけではないのです。書き込むタイミングは装置や状況に応じてシステムが決定する問題です。しかし、flush() メソッド用いれば、バッファ内のデータを強制的に、その時点で書き込むことができるのです。これをフラッシュと呼びます。close() メソッドでストリームを解放した時点でもフラッシュされます。
出力ストリームはこのほかに、メモリ上のバイト配列にデータを出力する ByteArrayOutputStream、出力データを変換する FilterOutputStream、 オブジェクトのデータを出力する ObjectOutputStream、通信パイプを生成するPipedOutputStream などが存在します。必要に応じて OutputStream を継承し、何らかのクラスにデータを出力するストリームを新たに生成しても良いでしょう。 各クラスについては Java2 API 仕様を参照してください。
10.2.2 Writerクラス
出力ストリームにデータを書き込むには、バイト配列として渡す必要がありました。バイナリデータを入出力する場合はこの方法が最適ですが、テキストデータを入出力する場合、文字列をバイト配列として扱うのは面倒です。そこで、文字列を出力ストリームに書き込む作業をサポートしてくれる Writer 抽象クラスを用いると便利です。Writer クラスは、出力ストリームにデータの書き込みをサポートするクラスのスーパークラスとなります。
メソッド | 解説 |
---|---|
abstract void close() | ストリームを閉じてフラッシュします。 |
abstract void flush() | ストリームをフラッシュします。 |
void write(char[] cbuf) | 文字の配列を書き込みます。 |
abstract void write(char[] cbuf, int off, int len) | off から len まで、文字の配列の一部を書き込みます。 |
void write(int c) | 単一文字を書き込みます。 |
void write(String str) | 文字列を書き込みます。 |
void write(String str, int off, int len) | off から len まで、文字列の一部を書き込みます。 |
表3を見てわかるように Writer クラスは文字列の書き込みをサポートすることを保障しています。より具体的なデータ型の書き込みは、このクラスを実装するクラスが専門に提供してくれます。中でも特に柔軟なのは PrintWriter クラスです。
コンストラクタ | 解説 |
---|---|
PrintWriter(OutputStream out) | 行の自動フラッシュは行わずに、既存の OutputStream から新しい PrintWriter を作成します。 |
PrintWriter(OutputStream out, boolean autoFlush) | 既存の OutputStream から新しい PrintWriter を作成します。 |
PrintWriter(Writer out) | 自動行フラッシュは行わずに、新しい PrintWriter を作成します。 |
PrintWriter(Writer out, boolean autoFlush) | 新しい PrintWriter を作成します。 |
メソッド | |
boolean checkError() | ストリームが閉じていなければフラッシュし、そのエラーの状態をチェックします。 |
void print(boolean b) | boolean 値を出力します。 |
void print(char c) | 文字を出力します。 |
void print(char[] s) | 文字の配列を出力します。 |
void print(double d) | 倍精度浮動小数点数を出力します。 |
void print(float f) | 浮動小数点数を出力します。 |
void print(int i) | 整数を出力します。 |
void print(long l) | long 整数を出力します。 |
void print(Object obj) | オブジェクトを出力します。 |
void print(String s) | 文字列を出力します。 |
void println() | 行区切り文字列を書き込むことによって、現在の行を終了します。 |
void println(boolean x) | boolean 値を出力して、行を終了します。 |
void println(char x) | 文字を出力して、行を終了します。 |
void println(char[] x) | 文字の配列を出力して、行を終了します。 |
void println(double x) | 倍精度浮動小数点数を出力して、行を終了します。 |
void println(float x) | 浮動小数点数を出力して、行を終了します。 |
void println(int x) | 整数値を出力して、行を終了します。 |
void println(long x) | long 整数を出力して、行を終了します。 |
void println(Object x) | Object を出力して、行を終了します。 |
void println(String x) | String を出力して、行を終了します。 |
protected void setError() | エラーが発生したことを示します。 |
すばらしいことに、このクラスは print() と println() メソッドを提供しています(表4)。私たちがこれまで何度も使ってきたこれらのメソッドを用いれば、あらゆるデータ型をテキストとして書き込むことができます。
import java.io.*; class Test { public static void main(String args[]) { try { OutputStream out = new FileOutputStream("test.txt"); PrintWriter writer = new PrintWriter(out); writer.println("Kitty on your lap"); writer.close(); out.close(); } catch(Exception err) { System.out.println(err); } } }
>java Test >type test.txt Kitty on your lap
コード2では、FileOutputStream クラスから生成した出力ストリームを PrintWriter() コンストラクタに渡しています。これによって、PrintWriter クラスの出力対象は、コンストラクタで指定したストリームとなります。あとは print() や println() メソッドを用いることで、すべてのプリミティブ型と参照型を文字列として書き込むことができます。最後に、Writer クラスの close() メソッドを呼び出すことを忘れないで下さい。