アルゴリズム工学データベース
---

標準入出力使用のすすめ

〜 フィルタの書き方 〜

---



 手抜きプログラム(?!)の基礎中の基礎、 標準入出力の使い方や使うメリットを紹介します。 Unix系を前提にしますが、MS-Windows系のDOS窓でも大部分通用します。

 フィルタが何か説明できる人にとっては、 常識しか書いてありませんので悪しからず。 (最後のヒヤドキュメントぐらいは価値があるかも。)


フィルタとは

 標準入出力を用いるプログラムです。 有名なところでは、'cat','sort','gzip'などなど、 日頃からお世話になっているコマンドの多くはフィルタなのです。


標準入出力とは

 shell からプログラムを起動すると、 「入力・出力・エラー出力」のために3つのファイルが準備されます。 これが標準**です。 せっかくはじめから用意してくれるのだから、 プログラマがこれを使わない手はありませんね。


普通の接続先 C C++ Java Fortran fileへのリダイレクト
標準入力 キーボード stdin cin System.in 5 command < file
標準出力 画面 stdout cout System.out 6 command > file(上書き)
command >> file(付け足し)
標準エラー出力 画面 stderr cerr/clog System.err 多くは0 (sh/bash)command 2> file
(csh/tcsh)command >& file

 特に何も指定しなければ、標準入力はキーボードですし、 標準出力と標準エラー出力は画面です。 なんと、キーボードや画面はファイルだったのです。


リダイレクト

 プログラムを起動する時に、ちょっと書き足すだけで 標準**の接続先を、キーボードや画面からファイルに変更することができます。 この操作をリダイレクションとかリダイレクトすると言います。 具体的な操作は上の表を見て下さい。

 さてさて、リダイレクトすると何が便利になるかと言いますと、 プログラムを作る手間が省けます。 出力結果をファイルに保存するために、 わざわざプログラム中でファイルをオープンする必要はないのです。 標準出力に出力するプログラムを作っておけば、 プログラムを使う人がファイルにリダイレクトすればよいのです。 (画面とファイルに同時出力する方法は後述します。) 入力についても然り。

 プログラム中でファイルをオープンする時、 ちゃんとエラー処理してますか? エラー処理を手抜きしてるなら、 それは手を抜く場所を間違えています。 最初からオープンされている標準入出力を使えばいいのです。 エラー処理はOSがやってくれます。


標準入出力の使い方

 では、標準入出力の使い方ですが、 C言語では、gets(), puts(), printf() などの関数が 標準入出力を扱います。いつもお世話になってますね。 特別なことは何もしないでいいのです。 明示的に書くなら、fで始まる関数を用いて fgets( stdin, ...), fprintf( stdout, ... ) などとなります。 標準エラー出力は fprintf( stderr, ... ) と書きます。 (ちなみにgets()とfscanf()は、悪名高いエラー処理の出来ない関数ですので、 それぞれfgets()とfgets()+sscanf()で置き換えましょう。)

 Fortran では、ハンドル番号が予約されています。 READ(5, ..), WRITE(6, ..), とすればよいようです。 困ったのが標準エラー出力。 WRITE(0, ..) とする処理系が多いようですが、 そうでない処理系も存在するようです。 移植性を優先するなら使えない、ということになります。

 いずれにしても、標準エラー出力は、 2つめの出力ファイルとして積極的に使うものではなく、 実行中の状態表示や、エラー/警告表示のためのものですので、 「ファイルにリダイレクトされない」つもりで使うのがよいでしょう。


パイプ

 標準入出力を用いると、プログラムを作る側が楽になることは既に説明しました。 しかし、プログラムを使う側にもメリットはあるのです。

 というわけで、パイプ処理を説明しましょう。 'command1 | command2'とすると、 'command1'の標準出力が'command2'の標準入力につながります。 2段階で書くと 'command1 > tempfile' 'command2 < tempfile' と同等ですが、パイプした方が簡単に書けますね。


外部プログラムとの連係

キーワード「less, sort, tee, gzip, zcat」

 パイプ処理の上手な利用法は、 外部プログラムといかに組み合わせるかにかかっています。 いくつか典型的な例を紹介します。

後戻りして見る
'command | less'で見れば、 画面の外に流れて行ったものも、戻って見ることもできますし、 検索もできます。
ソート
'command | sort'で行単位でソートされます。 ちなみに'sort +5'とすると、 「1行の中の(空白で区切られた)6つめのフィールドでソート」 されます。詳しくは'man sort'をどうぞ。
表示しながらファイルに保存
'command | tee outputfile'とすると、 画面表示しながら、同じ内容をファイルに書き込んでくれます。 これはなかなか便利ですよ。
データ圧縮
'gzip'と組み合わせると入力/出力ファイルを圧縮できます。
 入力データが冗長なら、 前もって'gzip inputfile'として圧縮しておき、 'gzip -cd inputfile.gz | command'とします。 'gzip -cd'の代わりに'zcat'としても同じことです。
 出力ファイルを圧縮するなら、 'command | gzip > outputfile.gz' とします。賢い less だと、普通に"outputfile.gz"を 見るだけで展開して内容表示してくれます。
 データが冗長で、1/10 以下に圧縮されるような時には、 この使い方は非常に効果的です。

標準入出力では力不足な時

 標準入出力は簡単かつ便利なものだということは 理解していただけたと思いますが、もちろん万能というわけではありません。 標準入出力だけでは対処できないのは、次の通りです。

 ファイルの数が足りないなら、 足りない分だけを自前でファイルオープンするか、 あるいは全ファイルを自前でファイルオープンするか、 どちらが適当かは状況次第でしょう。

 2つめの出力ファイルとして標準エラー出力を使うことは可能ではありますが、 前述した通り、標準エラー出力は 「ファイルにリダイレクトされない=画面に表示される」 ことが期待されているので、 2つめの出力ファイルとしては不適当です。


ファイルの指定がなければ標準入出力

キーワード「FILE*型」

 ファイルの指定があればそのファイルを使う、 指定がなければ標準入出力を使うという、 ちょっと凝ったプログラムもたくさんあります。 これを実現するC言語での典型的な処理例を書いておきます。 stdin/stdoutFILE*型であることが本質です。


#include <stdio.h>
#include <stdlib.h>

....

char *filename;              /* NULL ならファイル指定なしとします */
FILE *fp;

....

if ( filename == NULL ) {    /* ファイル指定なし */
    fp = stdin;
} else {                     /* ファイル指定あり */
    fp = fopen( filename, "r" );
    if ( fp == NULL ) {
        fprintf( stderr, "'%s'が読み込めません.\n", filename );
        exit(1);             /* 異常終了 */
    }
}

....



標準入力の内容をスクリプトに書く

キーワード「ヒヤドキュメント」

 Unix でしか通用しませんが、shやperlでは、 標準入力の接続先をキーボードでもファイルでもなく、 スクリプト内に書いておけます。 ヒヤドキュメントという機能ですが、 'command <<区切り単語''区切り単語'と書きます。 詳しくは'man sh'"Here Documents"を検索してみて下さい。 ここでは簡単なシェルスクリプトを紹介しておきます。


#!/bin/sh

cat <<EOF
This is test.

EOF

sort <<this-is-end
EOF
345
321
123
678
this-is-end


 実行結果は次のようになります。


This is test.

123
321
345
678
EOF



最後に

 もともとOSの持つ機能を使って、簡単にプログラムを作ると楽しいですね。 しかも、他の人にも使いやすいプログラムが作れます。 もっと楽な方法があれば、うーん、教えて下さい。


データベース [ 離散最適化 | グラフ | 幾何 | 並列/分散 ]
[ キーワード検索 ] [ リンク集 ] [ 動作確認環境 ]
参加メンバー [ 可視化 | パッケージ製作 | デバッグ術 | 標準入出力 | Makefile | 雛型 ]

土村 展之(tutimura@logopt.com)
<最終更新日 2002年4月21日 17時28分>