ファイル入出力

ファイル入出力についてみていきます。ファイルへのアクセス方法は対象となるファイルによりバイトストリーム(バイナリファイル)と文字ストリーム(テキストファイル)に大別されます。「ストリーム」とは「データの流れ」を表現する用語です。


ファイルオブジェクト

ファイル入出力の前に、まずファイルやディレクトリの取り扱いをみていきます。Javaでは「java.io.File」クラスによってファイルやディレクトリを1つのオブジェクトとして取り扱っています。「java.io.File」の詳細についてはJDK API仕様に譲るとして、ここでは簡単な使用例で確認してみます。


Fileオブジェクトの使用例

・サンプルソース(Sample1601.java)

import java.io.File;
import java.io.IOException;

public class Sample1601 {
    public static void main(String[] args) throws IOException {
        // ファイルの作成
        File fileObject1 = new File("C:\\dev\\java\\newFile1.txt"); //← "\"は"\\"とする必要があります。
        fileObject1.createNewFile();
        // ディレクトリの作成
        File dirObject = new File("C:\\dev\\java\\newDir");
        dirObject.mkdir();
        // 作成したディレクトリの中にファイルを作成
        File fileObject2 = new File(dirObject, "newFile2.txt");
        fileObject2.createNewFile();
        // ファイルの存在チェック
        if (fileObject1.exists()) {
            System.out.println(fileObject1.getName() + "は存在します。");
        if (fileObject1.isFile())
            System.out.println(fileObject1.getName() + "はファイルです。");
        } else {
            System.out.println(fileObject1.getName() + "は存在しません。");
        }
        // ディレクトリの存在チェックおよびその内部参照
        if (dirObject.exists()) {
            System.out.println(dirObject.getName() + "は存在します。");
            if (dirObject.isDirectory()) {
                System.out.println(dirObject.getName() + "はディレクトリです。");
                System.out.println(dirObject.getName() + "の中身は次のとおりです。");
                String[] ary = dirObject.list();
                for (int i = 0; i < ary.length; i++) {
                    System.out.println(ary[i]);
                }
            }
        } else {
            System.out.println(dirObject.getName() + "は存在しません。");
        }
    }
}

・実行結果

C:\dev\java>javac Sample1601.java [Enter]

C:\dev\java>java Sample1601 [Enter]
newFile1.txtは存在します。
newFile1.txtはファイルです。
newDirは存在します。
newDirはディレクトリです。
newDirの中身は次のとおりです。
newFile2.txt

C:\dev\java>dir  ← 実際のディレクトリ内を確認。
 ドライブ C のボリューム ラベルは Local Disk です
 ボリューム シリアル番号は 001A-E4E3 です

 C:\dev\java のディレクトリ

2008/03/16  23:58    <DIR>          .
2008/03/16  23:58    <DIR>          ..
2008/03/16  23:58    <DIR>          newDir  ← ディレクトリが作成されている。
2008/03/16  23:58                 0 newFile1.txt  ← ファイルが作成されている。
2008/03/16  23:58             1,473 Sample1601.class
2008/03/16  23:53             1,637 Sample1601.java
               3 個のファイル               3,110 バイト
               3 個のディレクトリ  52,261,048,320 バイトの空き領域

C:\dev\java>dir newDir  ← 作成したディレクトリ内を確認。
 ドライブ C のボリューム ラベルは Local Disk です
 ボリューム シリアル番号は 001A-E4E3 です

 C:\dev\java\newDir のディレクトリ

2008/03/16  23:58    <DIR>          .
2008/03/16  23:58    <DIR>          ..
2008/03/16  23:58                 0 newFile2.txt  ← ファイルが作成されている。
               1 個のファイル                   0 バイト
               2 個のディレクトリ  52,261,048,320 バイトの空き領域
        

▲PageTop

ファイル入力

Javaにはファイル入力関連のAPIが多く用意されていますが、ここでは主要である次のクラスを使用します。読み込み方としてバイト毎に行う方法(バイトストリーム)と文字ごとに行う方法(文字ストリーム)の2種類が存在します。

ファイル入力クラス構成
ファイル入力クラス一覧(各クラスの詳細はJDK API仕様を参照してください。)
クラス名 処理
FileInputStream 1バイト単位でバイトコードを読み込むときに使用します。
BufferedInputStream 1バイト単位ではなく、一度に多数のバイトを読み込み(バッファリング)、処理効率を上げるためのクラスです。
BufferedReader 1バイト単位で読み込み及び文字変換を行うのではなく、一度に多数のバイトを読み込み(バッファリング)、処理効率を上げるためのクラスです。
InputStreamReader 1バイト単位でバイトコードを読み込んで文字に変換するためのクラスです。文字エンコーディングを省略した場合はプラットフォームのデフォルトの文字エンコードが使用されます。
FileReader テキストを読み込むための簡易クラスです。プラットフォームのデフォルトの文字エンコードが使用されます。

バイトストリーム入力

ファイル(Sample1601.txt)からバイトコードとしてデータを読み込む方法を示します。読み込み方として、バイト単位で読み込む方法と読み込み処理の効率を図るために複数バイトを一度に読み込む方法(バッファリング)があります。


・サンプルテキスト(c:\dev\java\Sample1601.txt)

abcあいう
        

・1バイト単位の読み込みサンプルソース(Sample1602.java)

import java.io.FileInputStream;
import java.io.IOException;

class Sample1602 {
    public static void main(String args[]) throws IOException {
        String inputFile = "c:\\dev\\java\\Sample1601.txt";
        FileInputStream fis = new FileInputStream(inputFile);
        // 読み込み処理。
        int code;
        while ((code = fis.read()) != -1) {
            System.out.print(Integer.toHexString(code));
        }
        fis.close(); // ストリーム使用後はクローズ処理を行います。
    }
}

・実行結果

C:\dev\java>javac Sample1602.java [Enter]

C:\dev\java>java Sample1602 [Enter]
61626382a082a282a4
        

「a」、「b」、「c」はASCIIコードで「61」、「62」、「63」、「あ」、「い」、「う」はシフトJISコードで「82a0」、「82a2」、「82a4」で現されます。


・バッファリングによるの読み込みサンプルソース(Sample1603.java)

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

class Sample1603 {
    public static void main(String args[]) throws IOException {
        String inputFile = "c:\\dev\\java\\Sample1601.txt";
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(inputFile));
        int code;
        while ((code = bis.read()) != -1) {
            System.out.print(Integer.toHexString(code));
        }
        bis.close();
    }
}

・実行結果

C:\dev\java>javac Sample1603.java [Enter]

C:\dev\java>java Sample1603 [Enter]
61626382a082a282a4
        

文字ストリーム入力

ファイル(Sample1601.txt)から1文字単位で読み込む方法とバッファリングによる読み込み方法を示します。


・1文字単位の読み込みサンプルソース(Sample1604.java)

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

class Sample1604 {
    public static void main(String[] args) throws IOException {
        String inputFile = "c:\\dev\\java\\Sample1601.txt";
        InputStreamReader isr = new InputStreamReader(new FileInputStream(inputFile), "SJIS");
        int data;
        while ((data = isr.read()) != -1) {
            System.out.print((char)data);
        }
        isr.close();
    }
}

上記のサンプルでは文字エンコードとして「SJIS」を使用しましたが、その他のサポートされているエンコーディングについてはJDKドキュメントを参照してください。

また、エンコーディングをプラットフォームのデフォルトのものを使用する場合には「FileReader」クラスで次のように簡略化できます。

・サンプルSample1604.javaの簡略版(Sample1605.java)

import java.io.FileReader;
import java.io.IOException;

class Sample1605 {
    public static void main(String[] args) throws IOException {
        String inputFile = "c:\\dev\\java\\Sample1601.txt";
        FileReader fr = new FileReader(inputFile);
        int data;
        while ((data = fr.read()) != -1) {
            System.out.print((char)data);
        }
        fr.close();
    }
}

・実行結果

C:\dev\java>javac Sample1604.java [Enter]

C:\dev\java>java Sample1604 [Enter]
abcあいう
C:\dev\java>javac Sample1605.java [Enter]

C:\dev\java>java Sample1605 [Enter]
abcあいう
        

・バッファリングによる読み込みサンプルソース(Sample1606.java)

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

class Sample1606 {
    public static void main(String[] args) throws IOException {
        String inputFile = "c:\\dev\\java\\Sample1601.txt";
        BufferedReader br = new BufferedReader(new FileReader(inputFile));
        int data;
        while ((data = br.read()) != -1) {
            System.out.print((char)data);
        }
        br.close();
    }
}

・実行結果

C:\dev\java>javac Sample1606.java [Enter]

C:\dev\java>java Sample1606 [Enter]
abcあいう
        

バッファリングによる入力処理効率の比較

次のプログラム(5kbのデータ読み込み処理)を用いてバッファリングによる処理効率を比較してみます。


・入力処理計測用サンプルソース(Sample1607.java)

import java.io.*;
import java.lang.reflect.Method;

class Sample1607 {
    static String inputFile = "c:\\dev\\java\\Sample1602.txt";
    public static void main(String args[]) {
        takeMeasures("byteRead");          // 1バイト単位でのバイトコードの読み込み処理。
        takeMeasures("byteBufferedRead");  // バッファリングによるバイトコードの読み込み処理。
        takeMeasures("textRead");          // 1文字単位での文字の読み込み処理。
        takeMeasures("textBufferedRead");  // バッファリングによる文字の読み込み処理。
    }
    // 1バイト単位でのバイトコードの読み込み処理。
    public static void byteRead() throws IOException {
        FileInputStream fis = new FileInputStream(inputFile);
        int code;
        while ((code = fis.read()) != -1) {
            Integer.toHexString(code);
        }
        fis.close();
    }
    // バッファリングによるバイトコードの読み込み処理。
    public static void byteBufferedRead() throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(inputFile));
        int code;
        while ((code = bis.read()) != -1) {
            Integer.toHexString(code);
        }
        bis.close();
    }
    // 1文字単位での文字の読み込み処理。
    public static void textRead() throws IOException {
        InputStreamReader isr = new InputStreamReader(new FileInputStream(inputFile), "SJIS");
        int data;
        while ((data = isr.read()) != -1) {
            Character.toChars(data);
        }
        isr.close();
    }
    // バッファリングによる文字の読み込み処理。
    public static void textBufferedRead() throws IOException {
        BufferedReader br = new BufferedReader(new FileReader(inputFile));
        int data;
        while ((data = br.read()) != -1) {
            Character.toChars(data);
        }
        br.close();
    }
    // 計測処理。
    private static void takeMeasures(String readPattern) {
        try {
            Method method = Class.forName("Sample1607").getMethod(readPattern); // リファクタリングを使用。
            long startTime = System.currentTimeMillis();
            method.invoke(null); // メソッドの実行。
            long endTime = System.currentTimeMillis();
            System.out.println(readPattern + " ReadTime : " + (endTime - startTime) + " msec");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

・実行結果

C:\dev\java>dir [Enter]
 ドライブ C のボリューム ラベルは Local Disk です
 ボリューム シリアル番号は 001A-E4E3 です

 C:\dev\java のディレクトリ

2008/03/17  00:31    <DIR>          .
2008/03/17  00:31    <DIR>          ..
2008/03/17  00:30             5,000 Sample1602.txt  ← 5kbのファイルで読み込み調査を行う。
2008/03/17  00:31             2,443 Sample1607.java
               2 個のファイル               7,443 バイト
               2 個のディレクトリ  52,261,150,720 バイトの空き領域

C:\dev\java>javac Sample1607.java [Enter]

C:\dev\java>java Sample1607 [Enter] 計測を3回行う。1回目
byteRead ReadTime : 31 msec
byteBufferedRead ReadTime : 16 msec
textRead ReadTime : 16 msec
textBufferedRead ReadTime : 0 msec

C:\dev\java>java Sample1607 [Enter] 2回目
byteRead ReadTime : 16 msec
byteBufferedRead ReadTime : 0 msec
textRead ReadTime : 31 msec
textBufferedRead ReadTime : 0 msec

C:\dev\java>java Sample1607 [Enter] 3回目
byteRead ReadTime : 16 msec
byteBufferedRead ReadTime : 0 msec
textRead ReadTime : 31 msec
textBufferedRead ReadTime : 0 msec
        

上記結果からバッファリングの効果が得られていることが判ります。

尚、計測値についてはシステムにより異なります。(計測マシンのスペック CPU:Pentium Dual-Core1.6GHz Mem:1.5GB OS:Win-XP Pro)

▲PageTop

ファイル出力

Javaにはファイル出力関連のAPIが多く用意されていますが、ここでは主要である次のクラスを使用します。書き込み方としてバイト毎に行う(バイトストリーム)方法と文字ごとに行う(文字ストリーム)方法の2種類が存在します。

ファイル出力クラス構成
ファイル出力クラス一覧(各クラスの詳細はJDK API仕様を参照してください。)
クラス名 処理
FileOutputStream 1バイト単位でバイトコードを書き込むときに使用します。
BufferedOutputStream 1バイト単位ではなく、一度に多数のバイトを書き込み(バッファリング)、処理効率を上げるためのクラスです。
BufferedWriter 1文字単位ではなく、一度に多数の文字を書き込み(バッファリング)、処理効率を上げるためのクラスです。
OutputStreamWriter 指定された文字エンコーディングに従いて1文字単位で書き込みをためのクラスです。文字エンコーディングを省略した場合はプラットフォームのデフォルトの文字エンコードが使用されます。
FileWriter テキストを書き込むための簡易クラスです。プラットフォームのデフォルトの文字エンコードが使用されます。

バイトストリーム出力

ファイル(Sample1603.txt)にバイトコードとしてデータを書き込む方法を示します。書き込み方として、バイト単位で書き込む方法と書き込み処理の効率を図るために複数バイトを一度に書き込む(バッファリング)方法があります。


・1バイト単位の書き込みサンプルソース(Sample1608.java)

import java.io.FileOutputStream;
import java.io.IOException;

class Sample1608 {
    public static void main(String args[]) throws IOException {
        String outputFile = "c:\\dev\\java\\Sample1603.txt"; // ファイルが存在しなければ作成されます。
        FileOutputStream fos = new FileOutputStream(outputFile); // 第二引数を省略時はファイルを上書きします。
        // FileOutputStream fos = new FileOutputStream(outputFile, true); // 第二引数がtrueの時ファイルに追記します。
        int[] code = {97, 98, 99, 130, 160, 130, 162, 130, 164}; //「abcあいう」のコードを10進数で表した値です。
        // 読み込み処理。
        for(int i=0; i<code.length; i++) {
            fos.write(code[i]);
        }
        fos.close(); // ストリーム使用後はクローズ処理を行います。
        System.out.println("出力しました。");
    }
}

・実行結果

C:\dev\java>javac Sample1608.java [Enter]

C:\dev\java>java Sample1608 [Enter]
出力しました。

C:\dev\java>dir [Enter]
 ドライブ C のボリューム ラベルは Local Disk です
 ボリューム シリアル番号は 001A-E4E3 です

 C:\dev\java のディレクトリ

2008/03/17  00:55    <DIR>          .
2008/03/17  00:55    <DIR>          ..
2008/03/17  00:55                 9 Sample1603.txt  ← ファイルが作成されている。
2008/03/17  00:55               725 Sample1608.class
2008/03/17  00:54               837 Sample1608.java
               3 個のファイル               1,571 バイト
               2 個のディレクトリ  52,259,086,336 バイトの空き領域
        

・実行後のファイル(Sample1603.txt)

abcあいう
        

・バッファリングによるの書き込みサンプルソース(Sample1609.java)

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

class Sample1609 {
    public static void main(String args[]) throws IOException {
        String outputFile = "c:\\dev\\java\\Sample1603.txt";
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outputFile));
        int[] code = {97, 98, 99, 130, 160, 130, 162, 130, 164};
        for(int i=0; i<code.length; i++) {
            bos.write(code[i]);
        }
        bos.close();
        System.out.println("出力しました。");
    }
}

・実行結果

C:\dev\java>javac Sample1609.java [Enter]

C:\dev\java>java Sample1609 [Enter]
出力しました。
        

・実行後のファイル(Sample1603.txt)

abcあいう
        

文字ストリーム出力

ファイル(Sample1603.txt)に1文字単位で書き込む方法とバッファリングによる書き込み方法を示します。


・1文字単位の書き込みサンプルソース(Sample1610.java)

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

class Sample1610 {
    public static void main(String args[]) throws IOException {
        String outputFile = "c:\\dev\\java\\Sample1603.txt";
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(outputFile), "SJIS");
        osw.write("abcあいう");
        osw.close();
        System.out.println("出力しました。");
    }
}

上記のサンプルでは文字エンコードとして「SJIS」を使用しましたが、その他のサポートされているエンコーディングについてはJDKドキュメントを参照してください。

また、エンコーディングをプラットフォームのデフォルトのものを使用する場合には「FileWriter」クラスで次のように簡略化できます。

・サンプルSample1610.javaの簡略版(Sample1611.java)

import java.io.FileWriter;
import java.io.IOException;

class Sample1611 {
    public static void main(String args[]) throws IOException {
        String outputFile = "c:\\dev\\java\\Sample1603.txt";
        FileWriter fw = new FileWriter(outputFile);
        fw.write("abcあいう");
        fw.write(System.getProperty("line.separator")); // 行区切り文字(改行)するとき。
        fw.close();
        System.out.println("出力しました。");
    }
}

・実行結果

C:\dev\java>javac Sample1610.java [Enter]

C:\dev\java>java Sample1610 [Enter]
出力しました。

C:\dev\java>javac Sample1611.java [Enter]

C:\dev\java>java Sample1611 [Enter]
出力しました。
        

・実行後のファイル(Sample1603.txt)

abcあいう
        

・バッファリングによる読み込みサンプルソース(Sample1612.java)

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

class Sample1612 {
    public static void main(String args[]) throws IOException {
        String outputFile = "c:\\dev\\java\\Sample1603.txt";
        BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile));
        bw.write("abcあいう");
        bw.newLine(); //「BufferedWriter」には行区切り文字(改行)のメソッドが用意されています。
        bw.close();
        System.out.println("出力しました。");
    }
}

・実行結果

C:\dev\java>javac Sample1612.java [Enter]

C:\dev\java>java Sample1612 [Enter]
出力しました。
        

・実行後のファイル(Sample1603.txt)

abcあいう
        

バッファリングによる出力処理効率の比較

次のプログラム(5kbのデータ書き込み処理)を用いてバッファリングによる処理効率を比較してみます。


・出力処理計測用サンプルソース(Sample1613.java)

import java.io.*;
import java.lang.reflect.Method;

class Sample1613 {
    static String outputFile = "c:\\dev\\java\\Sample1603.txt";
    public static void main(String args[]) {
        takeMeasures("byteWrite");          // 1バイト単位でのバイトコードの書き込み処理。
        takeMeasures("byteBufferedWrite");  // バッファリングによるバイトコードの書き込み処理。
        takeMeasures("textWrite");          // 1文字単位での文字の書き込み処理。
        takeMeasures("textBufferedWrite");  // バッファリングによる文字の書き込み処理。
    }
    // 1バイト単位でのバイトコードの書き込み処理。
    public static void byteWrite() throws IOException {
        FileOutputStream fos = new FileOutputStream(outputFile);
        for(int i=0; i<50000; i++) {
            fos.write(97);
        }
        fos.close();
    }
    // バッファリングによるバイトコードの書き込み処理。
    public static void byteBufferedWrite() throws IOException {
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outputFile));
        for(int i=0; i<50000; i++) {
            bos.write(97);
        }
        bos.close();
    }
    // 1文字単位での文字の書き込み処理。
    public static void textWrite() throws IOException {
        FileWriter fw = new FileWriter(outputFile);
        for(int i=0; i<50000; i++) {
            fw.write("a");
        }
        fw.close();
    }
    // バッファリングによる文字の書き込み処理。
    public static void textBufferedWrite() throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter(outputFile));
        for(int i=0; i<50000; i++) {
            bw.write("a");
        }
        bw.close();
    }
    // 計測処理。
    private static void takeMeasures(String writePattern) {
        try {
            Method method = Class.forName("Sample1613").getMethod(writePattern); // リファクタリングを使用。
            long startTime = System.currentTimeMillis();
            method.invoke(null); // メソッドの実行。
            long endTime = System.currentTimeMillis();
            System.out.println(writePattern + " WriteTime : " + (endTime - startTime) + " msec");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

・実行出力結果

C:\dev\java>javac Sample1613.java [Enter]

C:\dev\java>java Sample1613 [Enter] 計測を3回行う。1回目
byteWrite WriteTime : 156 msec
byteBufferedWrite WriteTime : 16 msec
textWrite WriteTime : 47 msec
textBufferedWrite WriteTime : 15 msec

C:\dev\java>java Sample1613 [Enter] 2回目
byteWrite WriteTime : 156 msec
byteBufferedWrite WriteTime : 0 msec
textWrite WriteTime : 47 msec
textBufferedWrite WriteTime : 15 msec

C:\dev\java>java Sample1613 [Enter] 3回目
byteWrite WriteTime : 156 msec
byteBufferedWrite WriteTime : 16 msec
textWrite WriteTime : 47 msec
textBufferedWrite WriteTime : 15 msec

C:\dev\java>dir
 ドライブ C のボリューム ラベルは Local Disk です
 ボリューム シリアル番号は 001A-E4E3 です

 C:\dev\java のディレクトリ

2008/03/17  01:28    <DIR>          .
2008/03/17  01:28    <DIR>          ..
2008/03/17  01:27            50,000 Sample1603.txt  ← 5kbのデータ出力が完了している。
2008/03/17  01:26             2,175 Sample1613.class
2008/03/17  01:26             2,273 Sample1613.java
               3 個のファイル              54,448 バイト
               2 個のディレクトリ  52,258,926,592 バイトの空き領域
        

上記結果からバッファリングの効果が得られていることが判ります。

尚、計測値についてはシステムにより異なります。(計測マシンのスペック CPU:Pentium Dual-Core1.6GHz Mem:1.5GB OS:Win-XP Pro)

▲PageTop