Pipe Streams

Revised: Mar./10th/2002

多くの言語でパイプと言う仕組みが提供されています。それらは一般に、あるプロセスの出力を別のプロセスの入力に繋げることです。

例えば UNIX 系 OS では、コマンド "cat /etc/passwd | wc -l" とすることで、 "cat /etc/passwd" の出力を "wc -l" の入力に与えることが出来ます。 Java では、文字ストリームのパイプとバイトストリームのパイプが用意されています。

文字ストリーム PipedReader
PipedWriter
バイトストリーム PipedInputStream
PipedOutputStream

例えば、メソッドの中で次のようにパイプの入り口と出口を作ります:

PipedWriter pipeOut = new PipedWriter();
PipedReader pipeIn = new PipedReader(pipeOut);

この例では、 pipeOut から取得したストリームはそのまま pipeIn から取得できます。つまり、書き込みの文字ストリームを読み込みの文字ストリームにつなげたことになります。

pipeOut をこのメソッド内で処理して戻り値に pipeIn を指定すれば、このメソッドの処理結果を書き込むストリームを、読み込みのストリームにパイプを通して繋げられるわけです。

例えば、これに続いて次のように書いたとします:

// ファイルのインスタンス化
FileReader source = new FileReader("words.txt");
// 読み込みバッファでラップ
BufferedReader in = new BufferedReader(source);

// 書き込みのパイプを PrintWriter でラップ
PrintWriter out = new PrintWriter(pipeOut);
// 読み込みバッファから読み込んで書き込み
String line;
while ((line = in.readLine()) != null) {
	out.println(line);
	out.flush();
}
out.close();

このプロセスで out に書き込まれたモノはパイプストリーム PipeOut に渡され、パイプを通って pipeIn から読み込むことが出来ます。従って、このメソッドで return pipeIn; とすれば、次の制御へパイプがつながるわけです。

このプロセスを実行するメソッドを Reader pipe(Reader source) として定義すれば、引数 source で受け取ったストリームを pipeOut に書き込んで、それを pipeOut に出力して返すことが可能です。

サンプル

次のサンプルは、テキストファイルに行番号を付加して、行を逆に並べて、更に行番号を付加するものです。この三つのプロセス間ではストリームがパイプでつながれています。

PipeTest.java:

import java.io.*;
import java.util.*;

class PipeTest {
	public static void main(String[] args) throws IOException {
		// ファイルのインスタンス化
		FileReader text = new FileReader(args[0]);

		// パイプを利用した複数の処理
		Reader nlText = nl(tac(nl(text)));

		// 標準出力
		BufferedReader in = new BufferedReader(nlText);
		String line;
		while ((line = in.readLine()) != null) {
			System.out.println(line);
		}
		in.close();
	}

	// パイプを利用した処理:行番号付加
	static Reader nl(Reader source) throws IOException {
		BufferedReader in = new BufferedReader(source);

		// 読み込みと書き込みのパイプをつなげる
		PipedWriter pipeOut = new PipedWriter();
		PipedReader pipeIn = new PipedReader(pipeOut);
		PrintWriter out = new PrintWriter(pipeOut);

		// out に処理
		String line;
		int i=0;
		while ((line = in.readLine()) != null) {
			out.println(++i + ": " + line);
			out.flush();
		}
		out.close();

		// 読み込みパイプを返す
		return pipeIn;
	}

	// パイプを利用した処理:逆順に並べ替え
	static Reader tac(Reader source) throws IOException {
		BufferedReader in = new BufferedReader(source);

		// 読み込みと書き込みのパイプをつなげる
		PipedWriter pipeOut = new PipedWriter();
		PipedReader pipeIn = new PipedReader(pipeOut);
		PrintWriter out = new PrintWriter(pipeOut);

		// out に処理
		Stack rtext = new Stack();
		String line;
		int i=0;
		while ((line = in.readLine()) != null) {
			rtext.push(line);
		}
		while (!rtext.empty()) {
			out.println(rtext.pop());
			out.flush();
		}
		out.close();

		// 読み込みパイプを返す
		return pipeIn;
	}
}

次の例は、同じディレクトリにテキストファイル test.txt が存在した場合の実行例です。テキストファイルはOS標準のエンコードで用意します。 Windows では Shift_JIS です。

test.txt:

こんにちは
Hello World.

さようなら
Bye World.

コマンドライン:

C:\IO>javac PipeTest.java

C:\IO>java PipeTest test.txt
1: 5: Bye World.
2: 4: さようなら
3: 3:
4: 2: Hello World.
5: 1: こんにちは

C:\IO>


Copyright © 2002 SUGAI, Manabu. All Rights Reserved.