BufferedReader Class

last modified: Jan./5th/2002

ストリームのラップ

Reader クラスを継承したサブクラスのストリームは16ビット文字ストリームになりますが、読み込むのは一文字ずつです。これでは、呼び出しごとにファイルからバイトを読み込み、文字型に変換し、そのたびに復帰するため、非常に効率が悪くなります。これに対して、テキストを1行ずつ読み込んで、効率を上げるストリームが BufferedReader です。インスタンス化する時に、 Reader クラスのサブクラスを引数に与えて、これを BufferedReader クラスでラッピングしているわけです。

継承関係:

java.lang.Object
  |
  +--java.io.Reader
        |
        +--java.io.BufferedReader

コンストラクタ:

BufferedReader(Reader in)
BufferedReader(Reader in, int sz)

引数は Reader 型ですが、これは抽象クラスであり、実際に指定するのはそのサブクラスになります。sz は作成するバッファのサイズです。

このように他のストリームを通して入出力するストリームを High-level I/O Streams と呼ぶことがあります。ほかのストリームをラップした場合は、外側のストリームから順番に close() します。外側のストリームが占有していたマシン・リソースから順番に解放していくことになります。

サンプル

UNIX では concatenate コマンド (cat) というものが用意されています。引数に指定したファイルの内容をコマンドラインにつなげて出力するコマンドです。次のサンプルは、引数のファイルの中身を出力する、 cat コマンドのようなサンプルです:

Cat.java:

import java.io.*;

class Cat {
	public static void main(String args[]) throws IOException {
		File inputFile = new File(args[0]);
		FileReader in = new FileReader(inputFile);
		BufferedReader br = new BufferedReader(in);

		String line;
		while ((line = br.readLine()) != null) {
			System.out.println(line);
		}

		br.close();
		in.close();
	}
}

実行時引数にファイル名を与えると、コマンドラインに引き続いて、指定したファイルの内容が出力されます。このサンプルでは標準出力への出力を、改行つき出力である println() で行っていることに注意してください。読み込み時に、もともとのファイルの改行コード(<LF> や <CR>)は削除されています。

File クラスは、ファイル名だけではなく、ディレクトリ、ボリュームラベル、パスも認識します。従って、引数にフルパス (full path) を指定すれば任意のファイルを参照できます。但し、ディレクトリ名などに空白類文字を含む場合は、二重引用符「"」でくくっておかないと、正しく認識されずに複数の引数だと解釈されます。

BufferedReader クラスのコンストラクタは Reader 型の引数をとりますが、 FileReader クラスは Reader クラスのサブクラスなので、問題なく代入できます。

このサンプルでは、 BufferedReader クラスの readLine() メソッドを用いました。このメソッドは、一行のテキストを読み込み文字列を返します。終端文字として \n\r を認識し、読み込みに終端文字は含めません。そして、ストリームの終わりに達していれば null を返します。行区切文字を含まないために、このサンプルでは改行文字 '\n' を明示的に出力しました。

C:\IO>javac Cat.java

C:\IO>java Cat test.txt
こんにちは
Hello World.

さようなら
Bye World.

C:\IO>

このサンプルでは、読み込むファイルの文字符号化方法は、システムのデフォルト文字符号化方法に一致している必要があります。そうでないと、コマンドラインには文字化けを起こして出力されます。

出力の文字符号化方法を明示したい場合は、前節で見たように、 InputStreamReader/OutputStreamWriter クラスを用います。

CatEUC.java:

import java.io.*;

class CatEUC {
	public static void main(String args[]) throws IOException {

		File inputFile = new File(args[0]);
		FileInputStream infile = new FileInputStream(inputFile);
		InputStreamReader in = new InputStreamReader(infile, "EUC_JP");
		BufferedReader br = new BufferedReader(in);

		String line;
		while ((line = br.readLine()) != null) {
			System.out.println(line);
		}

		br.close();
		in.close();
		infile.close();

	}
}

EUC-JP で符号化されたテキストファイル test.txt が存在した場合の実行例です:

CatEUC.java の実行例

Cat.java で実行すると文字化けが起こっていますが、 CATEUC.java で実行した場合は、文字化けが起こりません。

UNIX 系 OS の cat コマンドは、ファイル名が複数与えられると、一つのものに結合して出力されます。このサンプルも、コマンドライン引数の個数を args.length で拾って、 for ループさせることで実現できます。試してみると良いでしょう。

サンプル

前節の最後に確認した、テキストファイルに行番号を付加するサンプルを、一文字ずつではなく、一行丸ごと扱うものを作ってみましょう:

Buffered.java:

import java.io.*;

public class Buffered {
	public static void main(String[] args) throws IOException {
		//ファイル名のインスタンス化
		File inputFile = new File("in.txt");
		File outputFile = new File("out.txt");

		//入出力ストリームのインスタンス化
		FileReader in = new FileReader(inputFile);
		FileWriter out = new FileWriter(outputFile);

		//FileReader の BufferedReader によるラッピング
		BufferedReader br = new BufferedReader(in);

		String str;
		int i = 1;
		while ((str = br.readLine()) != null) {
			out.write("\n"+i+": ");
			out.write(str);
			i++;
		}

		br.close();
		in.close();
		out.close();
	}
}

前節のサンプルよりもすっきりしています。

出力例は示しにくいのですが、たとえば in.txt が次の内容だったとします:

in.txt:

In the beginning God created the heaven and the earth.
And the earth was without form, and void; and darkness was upon the face of the deep. And the Spirit of God moved upon the face of the waters.
And God said, Let there be light: and there was light.
And God saw the light, that it was good: and God divided the light from the darkness.
And God called the light Day, and the darkness he called Night. And the evening and the morning were the first day.

このテキストファイルが存在するディレクトリで、 Buffered.java を実行すると次のような出力ファイル out.txt が生成されます:

out.txt:

1: In the beginning God created the heaven and the earth.
2: And the earth was without form, and void; and darkness was upon the face of the deep. And the Spirit of God moved upon the face of the waters.
3: And God said, Let there be light: and there was light.
4: And God saw the light, that it was good: and God divided the light from the darkness.
5: And God called the light Day, and the darkness he called Night. And the evening and the morning were the first day.

Reader クラスのラッピング

上のサンプルでは分かりやすくするために、インスタンス化の手続きを分けて書きましたが、通常は以下のようにまとめてしまいます:

BufferedReader br
	= new BufferedReader(new FileReader(new File(args[0])));
...中略...
br.close();

FileReader のオブジェクトを閉じる代わりに、 BufferedReader のオブジェクト br を閉じています。 FileReader 型オブジェクトの参照は、参照型変数に代入されているわけではなく、 br の引数に使われており、 br が参照するオブジェクトのインスタンスとして保持されています。

したがって、 br に関連するメモリなどのリソースを解放しても良いと指示すること (br.close()) は、引数の FileReader 型オブジェクトに関連するリソースの解放も指示することになります。



Copyright © 2002 SUGAI, Manabu. All Rights Reserved.