Reader, Writer の応用last modified: Sep./09th/2002
文字データの入出力は BufferedReader と BufferedWriter を用います。これらは文字ストリームをラッピングすることで働きます。その文字ストリームはコンストラクタに引数として与えますが、 Reader/Writer クラス型がコンストラクタの引数に定義されています。これらのクラスを継承したクラス型のストリームが引数として許されることになりますが、いろいろなパターンが考えられます。以下ではその点に注意してください。
| クラス名 | コンストラクタの引数の型に許されるクラスの一例 |
|---|---|
| BufferedReader | BufferedReader, CharArrayReader, FilterReader, InputStreamReader, PipedReader, StringReader |
| BufferedWriter | BufferedWriter, CharArrayWriter, FilterWriter, OutputStreamWriter, PipedWriter, PrintWriter, StringWriter |
次の例は、標準入力 System.in から読み込んだ文字列を、標準出力 System.out に書き込みます。
System.in を InputStreamReader でラップして文字ストリームに変換し、それを BufferedReader でラッピングします。同様に、 System.out を OutputStreamWriter でラップし、これを BufferedWriter でラッピングします。
import java.io.*;
class StdIO {
public static void main(String[] args) {
BufferedReader bin
= new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bout
= new BufferedWriter(new OutputStreamWriter(System.out));
String str="";
try {
for (int i=1 ;true ;i++) {
System.out.print("行> ");
str=bin.readLine();
if (str.equals("x")) {
break;
}
bout.write("\t"+i+": ");
bout.write(str);
bout.newLine();
}
bout.flush();
} catch (IOException e) {
System.out.println(e);
}
}
}
行> "に続いて文字列を入力すると、最後に出力します。x" を入力すると繰り返しを脱出します。flush() の位置を工夫しました。
C:\java>javac StdIO.java
C:\java>java StdIO
行> pride
行> greed
行> lust
行> anger
行> gluttony
行> envy
行> sloth
行> x
1: pride
2: greed
3: lust
4: anger
5: gluttony
6: envy
7: sloth
IODemo.java:
class IODemo {
public static void main(String[] args) {
java.io.BufferedReader bin
= new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
try {
while (true) {
System.out.print("半径:");
String str = bin.readLine();
double r = Double.parseDouble(str);
System.out.println("円周:" + 2.0 * Math.PI * r);
}
} catch (NumberFormatException ex) {
try {
bin.close();
} catch (java.io.IOException e) {
e.printStackTrace();
}
System.out.println("終了");
} catch (java.io.IOException ex) {
try {
bin.close();
} catch (java.io.IOException e) {
e.printStackTrace();
}
return;
}
}
}
Javaのデータ入出力は、ストリームと呼ばれるオブジェクトとして扱います。入力ストリームと呼ばれるオブジェクトからデータを読み込み、同じく出力ストリームと呼ばれるオブジェクトを通してデータを出力します。
このサンプルでは、標準入力ストリームSystem.inをInputStreamReaderのコンストラクタ引数にとっています。これを「ラップする」と呼びます。System.inをInputStreamReaderでラップし、更にBufferedReaderでラップしています。
入出力ストリームは、用が無くなればクローズする必要があります。ここでは、BufferedReaderオブジェクトbinをクローズしています。これをクローズすれば、ラップされているInputStreamReaderオブジェクトの同時にクローズされます。
データの入出力では、java.io.IOExceptionが発生し、これはjava.lang.Exceptionの直接のサブクラスであり、例外処理が必須です。ここではtry catch構文で処理しました。次のようにmain()メソッドのthrowsリストに加えて、処理を放棄する事も可能です。throwsリストに加えられた型の例外は、そのメソッドを呼び出した元のメソッドに処理の責任が委譲されます。main()メソッドはJavaVMによってアプリケーション起動時に最初に呼ばれており、呼び出し元のメソッドは存在しないので、誰も処理しない事になります。
class IODemo {
public static void main(String[] args) throws java.io.IOException {
java.io.BufferedReader bin
= new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
try {
while (true) {
System.out.print("半径:");
String str = bin.readLine();
double r = Double.parseDouble(str);
System.out.println("円周:" + 2.0 * Math.PI * r);
}
} catch (NumberFormatException ex) {
bin.close();
System.out.println("終了");
}
}
}
ここまではあえて完全限定名でクラスを指定していましたが、java.IO パッケージのクラスをimportしておけば、次のようにクラス名だけで指定できます。
import java.io.*;
class IODemo {
public static void main(String[] args) throws Exception {
BufferedReader bin
= new BufferedReader(new InputStreamReader(System.in));
try {
while (true) {
System.out.print("半径:");
String str = bin.readLine();
double r = Double.parseDouble(str);
System.out.println("円周:" + 2.0 * Math.PI * r);
}
} catch (NumberFormatException ex) {
bin.close();
System.out.println("終了");
}
}
}
コンパイル/実行結果
>javac IODemo.java >java IoDemo 半径:1 円周:6.283185307179586 半径:0.5 円周:3.141592653589793 半径: 終了
以下の例は、引用元のファイルから読み込んだ文字列を、小文字に変換して出力し直すものです。
import java.io.*;
class L2s {
public static void main(String[] args) {
try {
BufferedReader br
= new BufferedReader(new FileReader(new File(args[0])));
BufferedWriter bw
= new BufferedWriter(new FileWriter(new File(args[1])));
String str;
while ((str=br.readLine()) != null) {
//小文字に変換
String strl = str.toLowerCase();
//バッファへ出力
bw.write(strl);
bw.newLine();
}
//終了処理
bw.flush();
br.close();
bw.close();
} catch (IOException e) {
System.out.println(e);
}
}
}
コマンドラインから引数を二つ取ります。第一引数は入力元のファイルで、第二引数は出力目標のファイルを参照します。同じディレクトリのファイル名だけではなく、ボリュームラベル、ディレクトリ名も含めたパスで指定できます。
コマンドライン引数を File クラスのコンストラクタ引数に与え、これを FileReader クラスのコンストラクタ引数に与え、これを BufferdeReader クラスでラップしています。
Writer についても同じような手順で BufferedWriter クラスでラップしています。