ストリーム(ライブラリ編)

stdio(標準入出力ライブラリ)

システムコールではバイト数単位しか読み書きができません。
文字単位、行単位、で、文字列を扱う操作には不向きです。

また、読み書きの処理を高速化するためにはバッファリングなども必要となります。

標準入出力ライブラリではこれらの作業を代わりに行ってくれます。

バッファリング

飲食店にいくと「お水のおかわりいかがでしょうか?」と聞かれます。

この時、水差しからコップに水を注いでくれますが、この水差しがバッファの概念です。

「水(バイト)がどれほど必要か分からない、とりあえず水差し(バッファ)いっぱいに補給しておきます。」

バッファリングモード

読み込む(水差しに補給)場合は上述の通りですが、書き込む(コップに給水)場合はどうでしょうか。
速度を求めるだけならコップに水を一杯給水すれば良いです。

ただし、お客様が水を半分ほど欲しい場合があります。
この場合は出力の速度よりもある程度まとまった分量を出力したほうが親切です。

例: 速度重視の場合(バッファモード)

  • ウェイター「お水のおかわりいかがでしょうか」
  • お客様「お願いします」
  • ウェイター「失礼します」
  • お客様「あ、それぐらいでいいです。」
  • ウェイター「...(水が一杯になるまで作業を止められない)」
  • お客様「...」

例: 出力結果を確認できる場合

  • ウェイター「お水のおかわりいかがでしょうか」
  • お客様「お願いします」
  • ウェイター「失礼します」
  • お客様「...」
  • ウェイター「...(コップに水が半分になったら一回やめよう)」
  • お客様「(それぐらいでいいかなー)」
  • ウェイター「もう少しお水を注ぎましょうか?」
  • お客様「これぐらいでちょうど良いです。」

実際は人間同士だと複雑な処理機構働くのでこんな会話しませんが、コンピューターは単純な命令しか
聞けませんし、言えませんのでコンピューターを擬人化するとたぶんこんな感じでしょう。

実際に書き込み処理は人間が見るケースを想定している事が多いので改行毎に出力したりします。
これはストリームの向こう側*1が端末の場合にそうなっていると思います。

これに対してアンバッファモードなるものも存在します。

stderr*2は書き込みを即座に出力します。
これはエラーメッセージやデバッグ処理は速度よりもすぐに出力されることのほうが重要だからです。

詳細は下記もご覧下さい。

man setvbuf

ちなみにPerlで開発しているときにwarnとprintの順番がときどき入れ替わったりしますが、その理由を考えてみましょう。

1. printで改行が使用されない

#!/usr/bin/perl
print 'print';
warn 'warn';

2. 実行結果

:!perl test.pl
warn at test.pl line 3.
print

3. printで改行が使用されている

#!/usr/bin/perl
print 'print', "\n";
warn 'warn';

4. 実行結果

:!perl test.pl
print
warn at test.pl line 3.

FILE型

システムコール編ではストリームへのアクセスにファイルディスクリプタ(整数)を使っていましたが
stdioではFILEという型へのポインタが存在します。

FILE型を使うと何がうれしいかというと、ファイルディスクリプタとstdioバッファの内部情報が入っています。
つまりバッファ機能が追加されています。

FILEはファイルディスクリプタをラップしている、とも言えます。

stdioでのFILE操作

システムコールのセクションは(2)でしたが、ライブラリ関数のセクションは(3)です。

fopen(3)

fopen, fdopen, freopen - ストリームを開く関数

#include <stdio.h>

FILE *fopen(const char *path, const char *mode);
FILE *fdopen(int fildes, const char *mode);
FILE *freopen(const char *path, const char *mode, FILE *stream);
fclose(3)


fclose - ストリームを閉じる

#include <stdio.h>

int fclose(FILE *fp);
SEE ALSO
% man 3 fopen
% man 3 fclose

バイト単位の入出力

fgetc(3)

fgetc, fgets, getc, getchar, gets, ungetc - 文字と文字列の入力

#include <stdio.h>

int fgetc(FILE *stream);

帰り値は整数値です。EOF(-1)が帰ってくる場合があります。
EOFはstdio.h内で-1と定義されているマクロの事です。

fputc(3)

fputc, fputs, putc, putchar, puts - 文字と文字列の出力

#include <stdio.h>

int fputc(int c, FILE *stream);
getc(3), putc(3)

fgetc(), fputc()は関数でしたが、これは同じ意味をもつマクロです。

getchar(3), putchar(3)

入力元、出力先がよく使われる標準入出力を固定したAPIです。

getchar()はgetc(stdin)と同等です。putchar(c)はputc(c, stdout)と同等です。

ungetc(3)

バイト単位で値をバッファに戻す。

#include <stdio.h>

int ungetc(int c, FILE *stream);

*1:スピードの向こう側を思い出した人、昭和生まれですね。

*2:これもストリームの一種だということはもう説明しなくていいですよね