I/O Streams

Revised: Feb./23rd/2003; Since: Jan./5th/2002

プログラムでは一般に、多くの種類のデータを、多くの種類の入力先から取得して、多くの種類の出力先へ書き込む必要が考えられます。 Java では、これらのデータの入出力 (I/O) は一般にストリームとして解釈することになります。入力元へのストリームを開いて情報を読み込み、出力先へのストリームを開いて情報を書き込み、必要が無くなればそれらのストリームを閉じます。

これらのストリームを扱うための標準クラスライブラリは、 java.io パッケージから提供されます。これらのクラスは一般に、取り扱うデータの種類に応じて文字ストリームとバイトストリームの二つに分けられます。

そして、これらのストリームはオブジェクトとして扱われます。

ストリームとはなんだろうか

ストリームとはデータの直線的な流れという概念であり、現代的なプログラミング言語では、入出力をストリームとして扱います。

例えば、ディスク上のファイルに向かうストリームがあるとき、そこに書き込んだデータはファイルに出力されます。ネットワークから流れ込むストリームからはネットワーク越しのデータを受け取ることができます。プログラマはストリームに対する入出力を考えるだけで、その流れ行く果ての、実デバイスに対しては考えなくて良くなります。

JavaではデータI/Oにストリームという概念を使います。java.ioパッケージ内のストリーム・クラスは、大きく分けると、バイト・ストリーム、文字ストリーム、オブジェクト・ストリームの三つなります。JavaのI/Oは、全てストリームのオブジェクトとして実装します。

java.ioパッケージ内のクラスは、デバイスI/O、ネットワークI/Oなどが統一的な枠組みで設計されています。全てのストリームは、バイト・ストリーム、文字ストリーム、オブジェクト・ストリームに分けられ、次の6つの抽象クラス/インタフェースに基づきます。

OutputStream抽象クラス
全てのバイト出力ストリームのスーパークラス。write()でデータを書き込みます。
InputStream抽象クラス
全てのバイト入力ストリームのスーパークラス。read()でデータを読み込みます。
Writer抽象クラス
全ての文字出力ストリームのスーパークラス。write()で文字配列を書き込みます。
Reader抽象クラス
全ての文字入力ストリームのスーパークラス。read()で文字配列を読み込みます。
ObjectOutputStreamクラス
Serializableインタフェースを実装するクラス型のオブジェクトを連続するバイト列に変換(シリアライズ、直列化)したものとプリミティブ型データをOutputStreamに書き出す。
ObjectInputStreamクラス
ObjectOutputStreamで書き出したオブジェクトを読み込んで復元する。

Javaでは、文字は2バイトのUNICODEで表現されます。この点で、文字列処理においては、バイトの連続(シーケンス)であるバイト・ストリームは効率が悪く、文字ストリームが要求されました。また、オブジェクトをファイルやDBに格納したり、ネットワーク越しにやり取りしたりするために、オブジェクト・ストリームが必要となりました。

また、J2SE v1.4では、さまざまな要件に応えてjava.nioパッケージが追加されました。今後何年も使われるだろうパッケージを、敢えてNew I/Oと命名したことに、ストリームの枠組みは変更しないで、拡張機能だけをnioに実装する意図が読めます。

まずjava.ioのクラス群の利用を検討し、解決できない要件/要求が発生したときに、ユーティリティとしてnioパッケージも検討してください。本稿ではv1.3と共通のjava.ioパッケージのクラス群について紹介します。

ストリーム概説

java.ioのストリーム・クラスは要件に応じて多岐に渡ります。バイトストリームはInputStream/OutputStream、文字ストリームはReader/Writerを継承しています。詳細はAPI仕様書をご覧いただくとして、ここではその全体を概観してみます。

バイト・ストリームと文字ストリームには、お互いにカウンター・パートとなるクラスが存在するものがあります。次に、代表的なストリームに関するそれぞれの対応を示します。

基本的なストリーム
バイト・ストリーム 文字ストリーム説明
入力
出力
BufferedInputStream
BufferedOutputStream
BufferedReader
CharArrayReader
入出力にバッファを使って効率を上げる
入力
出力
ByteArrayInputStream
ByteArrayOutputStream
CharArrayReader
CharArrayWriter
StringWriter
バイト配列や文字配列、String型オブジェクトに対するストリームを作成する
入力
出力
FileInputStream
FileOutputStream
FileReader
FileWriter
ファイルに対するストリームを作成する
入力
出力
FilterInputStream
FilterOutputStream
FilterReader
FilterWriter
これらを継承してカスタムストリームを作成するための抽象クラス
入力
出力
PipeInputStream
PipeOutputStream
PipeReader
PipeWriter
複数の異なるストリーム間をつなげるパイプを作成する

java.io.*, java.swing.*などの代表的なストリームの階層構造は図3のようになります。非推奨のクラス、特殊なインタフェースやクラスなどは除いてあります。

ストリームのクラス図
図3. ストリームの階層構造(IDG Java 2 J2SE 1.4より転載)

文字ストリーム

文字コード

Java では、文字は Unicode で扱います。 Unicode で文字符号化された文字セットは一文字が16ビット(2バイト)で表現されます。そのコンピュータ上でのフォーマットは UTF と呼ばれます。因みに、 UTF は UCS Transformation Format であり、 UCS (Universal Character Set) を Transform した Format です。

下位7ビットは ASCII と互換性を持ちます。英米圏の ASCII で満足してきた人々は Unicode を Internationalization (I18N) を満足するものとして歓迎しています。しかし、日本や中国など、膨大な文字を必要とする言語圏での実用においては、多くの問題が指摘されており、必ずしも I18N のキラー・ソリューションではありません。

いずれにせよ、 JavaVM は文字/文字列の内部表現に Unicode を使い、入出力ではそのフォーマットである UTF を用います。

文字ストリーム

16ビット文字を読み書きするストリームは文字ストリームと呼ばれています。

文字ストリームにも多くの種類が存在しますが、それらは、Reader クラスと Writer クラスをスーパークラスとするサブクラスになっています。因みに、 Reader/Writer クラスは抽象クラスであり、直接インスタンス化することはできません。

java.io.Reader
Reader
  |
  +--BufferedReader------LineNumberReader
  |
  +--CharArrayReader
  |
  +--InputStreamReader---FileReader
  |
  +--FilterReader--------PushBackReader
  |
  +--PipeReader
  |
  +--StringReader
java.io.Writer
Writer
  |
  +--BufferedWriter
  |
  +--CharArrayWriter
  |
  +--OutputStreamWriter--FileWriter
  |
  +--FilterWriter
  |
  +--PipeWriter
  |
  +--StringWriter

例えば、 Reader クラスは読み込みのためのメソッドを次のように定義しています。

int read()
int read(char cbuf[])
int read(char cbuf[], int offset, int length)

確かに16ビット文字の読み込みになっています。同じく、 Writer クラスは書き込みのメソッドを次のように定義しており、16ビット文字を扱っていることが分かります。

int write(int c)
int write(char cbuf[])
int write(char cbuf[], int offset, int length)

バイトストリーム

16ビット文字の I/O に対して、画像や音などのバイナリー・データを扱うストリームはバイトストリームと呼ばれています。これらのクラスは、 InputStream クラスと OutputStream から派生しています。

java.io.InputStream
InputStream
  |
  +--FileInputStream
  |                         +--LineNumberInputStream
  +--PipedInputStream       |
  |                         +--DataInputStream
  +--FilterInputStream------|
  |                         +--BufferedInputStream
  +--ByteArrayInputStream   |
  |                         +--PushbackInputStream
  +--SequenceInputStream
  |
  +--StringBufferInputStream
  |
  +--ObjectInputStream
java.io.OutputStream
OutputStream
  |
  +--FileOutputStream
  |
  +--PipedOutputStream      +--DataOutputStream
  |                         |
  +--FilterOutputStream-----+--BufferedOutputStream
  |                         |
  +--ByteArrayOutputStream  +--PrintStream
  |
  +--ObjectOutputStream

Reader/Writer と同じく、 InputStream/OutputStream の読み込み/書き込みのメソッドを見てみましょう。

最初は InputStream クラスで定義されている読み込みのメソッドです:

int read()
int read(byte cbuf[])
int read(byte cbuf[], int offset, int length)

次に OutputStream の書き込みのメソッドの定義を見てみます。

int write(int c)
int write(byte cbuf[])
int write(byte cbuf[], int offset, int length)

どちらもバイト型でデータを取り扱っていることが分かります。



Copyright © 2002-2003 SUGAI, Manabu. All Rights Reserved.