標準ストリームの使い方

こんにちは、めのんです!

前回前々回とファイルについて解説してきています。
今回もその関連になります。
ファイルというよりはストリームですね。

標準ストリームのためのマクロ

「標準ストリーム」という表現は規格の中には出てきませんが、私は便宜的にそのように呼んでいます。
ここでいう「標準ストリーム」は、「標準入力」、「標準出力」、「標準エラー」の総称だと考えてください。

この概念自体はすごく一般的なものなので、PHPでWebアプリケーションしかやったことがない方でもご存じじゃないかと思います。

PHPにはSTDIN、STDOUT、STDERRという定数が用意されています。
CLI専用の定数なので、もしかしたらご存じない方もいらっしゃるかもしれませんね。

Cにはそれらと対応する「stdin」、「stdout」、「stderr」という3つのマクロが「stdio.h」ヘッダで定義されています。
それぞれ標準入力、標準出力、標準エラーに対応するものですが、見ればどれがどれかわかりますよね。

これらのマクロはFILE型へのポインタに評価されますので、fopen関数が返したFILE型のポインタと同じように使うことができます。
stdin、stdout、stderrの3つのマクロはmain関数が呼び出されるより前にオープンされて、プログラムの終了時に自動的にクローズされます。

stdinとstdoutのための入出力関数には専用のものがたくさんあって、それらの関数では明示的にstdinやstdoutを指定する必要がありません。

たとえば、scanf関数はstdinに対するものですし、printf関数はstdoutに対するものです。
ほかにも、gets関数とputs関数、getchar関数とputchar関数あたりがそれに該当します。

fgets関数とfputs関数のように、stdinやstdout専用のものがない関数もありますので、そういうものは明示的にstdinやstdoutを指定する必要があります。

ところで、PHPではfopen関数で

$stdin = fopen('php://stdin', 'r');

のような形で標準ストリーム(PHPでは入出力ストリームと呼ぶようですね)をオープンできますが、Cにはそのような方法がありません。
ここは注意が必要だと思います。

freopen関数で標準ストリームにファイルを結びつける

標準入力、標準出力、標準エラーはOS(というかシェル)の機能を使ってリダイレクトができることはご存じかと思います。
Cのプログラムの中でもそれと同じようなことができます。
というより、これから紹介するのと似たような方法でリダイレクトが実現されています。

Cにはfreopen関数というものがあります。
この関数は「stdio.h」ヘッダで次のように宣言されています。

FILE *freopen(const char * restrict filename,
              const char * restrict mode,
              FILE * restrict stream);

ちょっと長かったので複数行にわけて書きましたが、1行で書いても同じことです。

この関数、fopen関数とよく似ているんですけど、3番目の引数でFILE型へのポインタを指定しているところが違います。
つまり、指定したstreamにオープンした結果のストリームを結びつけるようになっています。
オープンに成功すればstreamを返しますし、失敗すればNULLを返す仕様です。

この関数を使えば、たとえば「sample.txt」というファイルをを標準入力を使って読み込めるようにできます。

if (freopen("sample.txt", "r", stdin) != NULL)
{
  char s[256];
  gets(s);  // stdinを使ってsample.txtから読み込む
}

ここでgets関数はセキュリティホールがどうとかいうのは議論の本質ではありませんよ。

こんな感じで、リダイレクトと同じようなことを自分のプログラムの中でできるようになります。

PHPではob_start関数で標準出力をバッファリングできると思います。
Cにはそういうのはないので、何らかのファイルに標準出力の結果を蓄えておいて、あとからそれを取り出すことならできます。

標準入力と標準出力が使えればCGIが作れる

実際にやるかどうかは別として、標準入力と標準出力が使えればCGIの形でWebアプリケーションを作ることもできます。
つまり、CでもWebアプリケーションを作れるということです。
もちろん、WebサーバーごとCで実装することもできますけどね。

標準出力に書き込んだ内容がそもままHTTPのレスポンスになりますし、POSTメソッドで送られてきた内容は標準入力を使って受け取れます。
環境変数として渡される情報もあるので、それらを組み合わせればCGIの基本的なことはすべてできるようになります。

ただし、PHPのように気を利かせてはくれませんので、Content-typeなどのHTTPヘッダも全部自分で出力しないといけませんので結構面倒だと思います。

できるできないでいえばCでもできるんですけど、それに特化した言語に比べればお膳立てされていることが少ないので全部自分でやることになります。

逆にいえば、そういうお膳立てをしてくれるライブイラリやフレームワークを用意してあげれば、Cでも結構頑張れるんじゃないかなと思います。
文字列の扱いが面倒なので、大変は大変でしょうけどね。


今回の解説は以上となります。
次回はファイルの削除と名前の変更を解説しようと思います。
どうぞご期待ください!