第20回 入出力処理(2) バイトストリーム



前回は文字列データをファイルへ入力したりファイルから出力したりする時に使用する文字ストリームについて学習しましたが、Javaで扱われるデータは文字列データばかりではありません。画像や音声といったバイナリデータ(binary data)も存在します。
今回説明するバイトストリーム(binary data)はこうしたバイナリデータを扱う為のものです。個々のバイナリデータを扱うだけでなく、クラスオブジェクトの変数等の内容を保存したり、逆に保存した内容を読み込むことも可能にするクラスも存在します。

■バイトストリーム
バイトストリームは文字では表現できないデータの入出力を行う為のストリームです。
java.ioパッケージで提供されているバイトストリームの主要なクラスの継承関係は次の通りです。
バイトストリームクラスの構成
Objectクラスのみjava.langパッケージに属しています。

入力用クラス名がReadではなくInput、出力用クラス名がWriteではなくOutputになっています。

■InputStreamとOutputStream
InputStreamクラスとOutputStreamクラスは文字ストリームのReaderとWriterに相当するクラスであり、共に抽象クラスです。
これらのクラスを直接インスタンス化して使用することはできませんが、全てのバイト入力/出力ストリームに使用できる機能が定義されている重要なクラスです。
InputStreamの主なメソッド
メソッド説明
void close()入力ストリームを閉じる
int read()入力ストリームから次のバイトデータを読み込み、
0〜255の範囲内の値として返す抽象メソッド。
読み込むデータがなければ-1を返す。
int read(byte buffer[])入力ストリームからバイト数を読み込みbyte配列bufferに格納する。
戻り値は実際に読み込まれたバイト数。
読み込むデータがなければ-1を返す。
long skip(n)nバイト分スキップして、
実際にスキップしたバイト数を返す

OutputStreamの主なメソッド
メソッド説明
void close()出力ストリームを閉じる
void write(int i)ストリームにiの8ビットを書き込む
(上位24ビットは無視される)
void write(byte buffer[])ストリームにbyte型配列bufferを書き込む


■FileInputStreamとFileOutputStream
FileInputStreamはInputStreamクラスを拡張・実装したクラスです。これを使うことでファイルからバイナリデータを読み取ることができるようになります。
FileInputStream
コンストラクタ意味
FileInputStream(String filepath)
throws FileNotFoundException
パス名filepathでFileInputStreamオブジェクト生成
FileInputStream(File fileobj)
throws FileNotFoundException
対象ファイルをFileオブジェクトfileobjで指定して
FileInputStreamオブジェクト生成
独自メソッド意味
なし。InputStreamから継承された
メソッドを使用可能。
FileOutputStreamはFileOutputStreamクラスを拡張・実装したクラスです。これを使ってバイナリデータをファイルに書き込むことができるようになります。
FileOutputStream
コンストラクタ意味
FileOutputStream(String filepath)
throws FileNotFoundException
パス名filepathでFileOutputStreamオブジェクト生成
FileOutputStream
(String filepath, boolean append)
throws FileNotFoundException
パス名filepathでFileOutputStreamオブジェクト生成
ファイルが既存でappendがtrueなら追加更新
FileOutputStream(File fileobj)
throws FileNotFoundException
対象ファイルをFileオブジェクトfileobjで指定して
FileOutputStreamオブジェクト生成
独自メソッド意味
なし。OutputStreamから継承された
メソッドを使用可能。
FileNotFoundExceptionはIOExceptionのサブクラスであり、検査例外です。その為IOException同様、これらをスローするメソッドを使用する場合は例外処理を記述しないとコンパイルエラーになります。

■FilterInputStreamとFilterOutputStream
FilterInputStreamはInputStreamを拡張したクラスで、既存の入力ストリームを格納します。そしてその入力ストリームをデータの基本的なソースとして使用し、データを途中で変換したり、追加機能を提供したりするのです。
FilterInputStream
コンストラクタ意味
FilterInputStream(InputStream is) InputStreamオブジェクトisを用いて
FilterInputStreamオブジェクトを生成
独自メソッド意味
なし。InputStreamから継承された
メソッドを使用可能。
一方FilterOutputStreamはOutputStreamを拡張したクラスで、既存の出力ストリームを格納します。そしてその出力ストリームをデータの基本的なソースとして使用し、データを途中で変換したり、追加機能を提供したりするのです。
FilterOutputStream
コンストラクタ意味
FilterOutputStream(OutputStream os) OutputStreamオブジェクトosを用いて
FilterOutputStreamオブジェクトを生成
独自メソッド意味
なし。OutputStreamから継承された
メソッドを使用可能。


■BufferedInputStreamとBufferedOutputStream
BufferedInputStreamはFilterInputStreamのサブクラスです。BufferedInputStreamはバイトストリームからの入力をバッファに格納します。
BufferedInputStream
コンストラクタ意味
BufferedInputStream(InputStream is) InputStreamオブジェクトisを用いて
BufferedInputStreamオブジェクトを生成
独自メソッド意味
なし。InputStreamから継承された
メソッドを使用可能。
BufferedOutputStreamはFilterOutputStreamのサブクラスです。BufferedOutputStreamはバイトストリームへの出力をバッファに格納します。
BufferedOutputStream
コンストラクタ意味
BufferedOutputStream(OutputStream os) OutputStreamオブジェクトosを用いて
BufferedOutputStreamオブジェクトを生成
独自メソッド意味
なし。OutputStreamから継承された
メソッドを使用可能。


■PrintStream
PrintStreamはFilterOutputStreamを拡張したクラスです。今まで何度も使ってきたSystem.out.printlnSystem.outもこのクラスのオブジェクトであり、println()もこのクラスのメソッドです。
PrintStream
コンストラクタ意味
PrintStream(File file) 指定ファイルfileで
新しいPrintStreamオブジェクトを生成
PrintStream(File file, String encoding)ファイルfileと文字セットencodingで
PrintStreamオブジェクトを生成
PrintStream(OutputStream os)出力ストリームosを元に
PrintStreamオブジェクトを生成
PrintStream
(OutputStream os, boolean autoFlush)
出力ストリームosを元にPrintStream
オブジェクトを生成
(autoFlushがtrueなら
行の自動フラッシュを行う)
PrintStream
(OutputStream os, boolean autoFlush,
String encoding)
出力ストリームosを元に文字セット
encodingでPrintStreamオブジェクトを生成
(autoFlushがtrueなら
行の自動フラッシュを行う)
PrintStream(String fileName)指定ファイル名fileNameで
新しい出力ストリームを
生成
PrintStream
(String fileName, String encoding)
指定ファイル名fileNameと文字セット
encodingでPrintStreamオブジェクトを生成
独自メソッド意味
void print(boolean b)引数を出力
void print(char c)
void print(char[] s)
void print(double d)
void print(float f)
void print(int i)
void print(long l)
void print(Object obj)
void print(String s)
void println(boolean x)引数を出力
(末尾に改行を付加)
void println(char x)
void println(char[] x)
void println(double x)
void println(float x)
void println(int x)
void println(long x
void println(Object x)
void println(String x)

■ObjectInputStreamを用いたシリアライズ
ObjectInputStreamはInputStreamを拡張したクラスです。
ObjectInputStream
コンストラクタ説明
ObjectInputStream
(InputStream is)
InputStreamオブジェクトisを用いてObjectInputStream
オブジェクトを生成
独自メソッド(一部)説明
void defaultReadObject()現在のクラスの非static及び非transient変数を
このストリームから読込
int read()データのバイトを読込
(ストリーム終端到達時は-1を返す)
boolean readBoolean()対応する型のデータを読込
byte readByte()
short readShort()
char readChar()
int readInt()
long readLong()
float readFloat()
double readDouble()
Object readObject()
これを用いることによりオブジェクト内のデータをバイトストリームに変換できます。変換されたデータはファイルに出力することも可能なので、オブジェクト内のデータをファイル内に保存することも可能になります。これがシリアライズ(serialize・シリアル化・直列化)です。
シリアライズを行うクラスには前もってjava.ioパッケージに含まれているSerializableインタフェースを実装する必要があります。
Serialzableインタフェースにはメソッドや変数などが存在しないので実装先のクラスにメソッドの実装などを行う必要はありません。このインタフェースはシリアライズ可能(serializable)であることを識別する為だけに機能しています。
Serializableを実装したクラスでも、全ての変数がシリアライズ対象になるわけではありません。static或いはtransient修飾子が付与された変数はシリアライズ対象外となります。
import java.io.Serializable
class SerializeX implements Serializable{
    String strName;
    int iData1;
    transient int iData2;
    static byte bData3;
}
このクラスSerializeXの場合、strName1とiData1のみがシリアライズ対象となります。

■ObjectOutputStreamを用いたデシリアライズ
ObjectOutputStreamはOutputStreamを拡張したクラスです。
ObjectOutputStream
コンストラクタ説明
ObjectOutputStream
(OutputStream os)
OutputStreamオブジェクトosを用いてObjectOutputStream
オブジェクトを生成
独自メソッド(一部)説明
void defaultWriteObject()現在のクラスの非static及び非transient変数を
このストリームから書込
void write(int data)データのバイトを書込
boolean readBoolean()対応する型のデータを書込
byte writeByte()
short writeShort()
char writeChar()
int writeInt()
long writeLong()
float writeFloat()
double writeDouble()
Object writeObject()
これを用いることにより、シリアライズによってファイルに出力されたオブジェクトのデータを読み込むことができます。これがデシリアライズ(deserialize)です。

■サンプルプログラム
FileInputStreamとFileOutputStreamのサンプルプログラムです。FileOutputStreamでバイトストリームをファイル内に出力し、その後FileInputStreamでそのファイルの内容を読み込みます。
/***** FileOutputStreamとFileInputStreamのテスト *****/

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class FileStreamTest extends java.lang.Object{
    static final String TEST_FILE = "C:\\Temp\\FileStreamTest.dat";
    public static void main(String [] args){
        int iCount;
        int iResult;
        try{
            //FileOutputStreamオブジェクトを用意
            File             f   = new File            (TEST_FILE);
            FileOutputStream fos = new FileOutputStream(f);
            
            //FileOutputStreamオブジェクトでファイルにバイトデータ書込
            for(iCount = 1; iCount <= 10; iCount++){
                fos.write(iCount);
            }
            
            //FileOutputStreamオブジェクトをクローズ
            fos.close();
            
            //FileInputStreamオブジェクトを用意
            FileInputStream fis = new FileInputStream(f);
            
            //FileInputStreamオブジェクトでファイルからバイトデータ読込
            do{
                iResult = fis.read();
                System.out.println(iResult);
            } while(iResult >= 0);
            
            //FileInputStreamオブジェクトをクローズ
            fos.close();
            f = null;
        }
        catch (FileNotFoundException e){
            System.out.println("FileNotFoundException例外発生");
            System.out.println(e);
        }
        catch (IOException e){
            System.out.println("IOException例外発生");
            System.out.println(e);
        }
        catch (Exception e){
            System.out.println("例外発生");
            System.out.println(e);
        }
    }
}

一方こちらはシリアライズとデシリアライズのサンプルプログラムです。シリアライズ/デシリアライズの対象になる変数とならない変数があることを確認してください。
import java.io.Serializable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/****** シリアライズされるクラス ******/
class SerializeX extends java.lang.Object implements Serializable{
              String strName;
              int    iData1;
    static    int    iData2;
    transient int    iData3;
    
    //コンストラクタ
    public SerializeX(String strName,
                      int    iData1,
                      int    iData2,
                      int    iData3){
        this.strName = strName;
        this.iData1  = iData1;
        this.iData2  = iData2;
        this.iData3  = iData3;
    }
    
    public SerializeX(String strName){
        this(strName, -1, -2 , -3);
    }
    
    public SerializeX(){
        this("nameless");
    }
}

/****** main()メソッドを含んだクラス ******/
public class SerializeTest extends java.lang.Object{
    static final String TEST_FILE = "C:\\Temp\\FileStreamTest.dat";
    public static void main(String [] args){
        //SerializeXオブジェクトを生成
        SerializeX x1 = new SerializeX("x1");
        x1.iData1 =   1;
        x1.iData2 =  20;
        x1.iData3 = 300;
        System.out.println("### シリアライズ前の変数 ###");
        System.out.println("strName = " + x1.strName);
        System.out.println("iData1  = " + x1.iData1);
        System.out.println("iData2  = " + x1.iData2);
        System.out.println("iData3  = " + x1.iData3);
        
        SerializeX x2 = new SerializeX();
        
        try{
            //SerializeXオブジェクトをシリアライズ
            FileOutputStream   fos = new FileOutputStream  (TEST_FILE);
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(x1);
            oos.close();
            fos.close();
            
            //生成されたファイルをデシリアライズ
            FileInputStream   fis = new FileInputStream  (TEST_FILE);
            ObjectInputStream ois = new ObjectInputStream(fis);
            x2 = (SerializeX)ois.readObject();
            ois.close();
            fis.close();
            System.out.println("### デシリアライズ結果 ##");
            System.out.println("strName = " + x2.strName);
            System.out.println("iData1  = " + x2.iData1);
            System.out.println("iData2  = " + x2.iData2);
            System.out.println("iData3  = " + x2.iData3);
        }
        catch (FileNotFoundException e){
            System.out.println("FileNotFoundException例外発生");
            System.out.println(e);
        }
        catch (IOException e){
            System.out.println("IOException例外発生");
            System.out.println(e);
        }
        catch (Exception e){
            System.out.println("例外発生");
            System.out.println(e);
        }
    }
}

質問はこちらへお願いします(件名はそのままで)。