Another introduction to programming language C 0C: ファイル入力

あまり褒められたことではないが,ときにはプログラムで,キーボードからの入力を受け付けたいことがあるだろう.例えば,整数をキーボード(生意気に「標準入力」と言う)から1個入力させ,それを変数aに入れたいとしよう.変数aは

int a = 0;

としてあらかじめ用意しておく.標準入力(キーボードの格好いい別名)からの入力は

scanf("%d", &a);

とする...などと書いてある教科書は今すぐ捨てなさい.正しい方法は

char buff[1000];  /* 読み込みバッファとして使う */
fgets(buff, 1000, stdin); /* 標準入力からいったんバッファに読み込む(最大1000字) */
sscanf(buff, "%d", &a);  /* バッファから数値を抜き出す */

である.標準入力からの読み込みは,一旦文字列として受け取るに限る.なぜならば,数値以外のデータがやってくるかもしれないからである.scanfは期待したもの以外がやってくるとパニックになる.

プログラムを解説していこう.char buff[1000]; は文字配列(つまりは文字列)の確保である.例として1,000文字分を確保した.これは行バッファ(1行ずつ読むための一時記憶)として使う.1行1,000文字を越えることは無かろうから,ひとまず十分な大きさである.(行バッファの大きさを越える行の取り扱いは初心者レベルを超えている.)

fgets(buff, 1000, stdin); は標準入力を表すポインタstdin(stdio.hヘッダで定義されている)が渡されているので,標準入力から最大で1,000文字をbuffへ流し込む.最大で,というのは,fgetsは改行文字(リターン)に出会うとそこで流し込むのを打ち切るからである.

sscanf(buff, "%d", &a); は,buffの中身をフォーマット文字列 "%d" と照らし合わせて,一致した部分を続く引数 (&a) に代入する.関数引数への代入であるから,変数名aには参照演算子&をつけて渡してやらないといけない.フォーマット文字列はだいたいprintfのそれと同じである.一点注意すべきなのは "%f" はprintfではdouble型を表すのに対してsscanfではfloat型(IEEE単精度浮動小数点)を表すことである.double型への読み込みは "%lf" を使う.(これは標準C言語 (C89/90) 以前のC言語 (K&R C) への互換性のためと思う.)

変数1個への代入では面白くないし,実用的でもない.配列に読み込みたい.ここでC言語が可変長の配列をサポートしないという壁につきあたる.そこで,大変残念なことだが,読み込む配列のサイズの上限を決めておこう.上限は後で変更できるようにNという定数マクロで決めておく.

#define N 1000

キーボードから最大1,000個の数値を読み込めることにした.今度は実数を受け付けることにしよう.

double *a = calloc(N, sizeof(double));
int i = 0;
while (!feof(stdin) && i < N) {
  char buff[1000];
  fgets(buff, 1000, stdin);
  sscanf(buff, "%lf", &a[i]);
  i += 1;
}

while文の条件式 !feof(stdin) && i < N について解説が必要であろう.まず A && B はAおよびBが真ならば真,それ以外なら偽である.(ブール代数を知っていれば&&は論理積であることがわかるだろう.)また!AはAが真ならば偽,偽ならば真である.!記号はnotと呼ぶ.

feof(...)はファイルが終端に達したら真(非ゼロ)を返す関数である.キーボードからの入力だと,たいていはCtrl-Dの入力がファイル終端を表すので,ユーザがCtrl-Dを押して入力の終わりを告げたかどうかの判定に使える.stdinは標準入力のことで,通常はキーボードである.(後で変更する方法を示す.)

while文の条件式に i < N をつけているのは,ファイル終端(Ctrl-Dの入力)が来る前に読み込み行数がNを越えてしまわないようにとの配慮である.もしiがNと等しくなったら,whileループを抜け出す.

さて,これまではキーボードからの入力だけを考えていた.しかし,本気でキーボードから1,000個もの数値を打ち込めるだろうか.もちろんそう思ってこのコードを書いたわけではない.数値はファイルから読み込みたい.

UNIXシステムの偉大さはここにある.何度も出てきた「標準入力」であるが,標準入力はキーボードだけではなく,ファイルも標準入力にすることができる.もしプログラムがtestという名前で,数値が並べられたファイルがnumbers.txtという名前ならば,UNIX系のシステムでは

$ ./test < numbers.txt

とするだけで,キーボードからではなくnumbers.txtからテキストが読み込まれる.

また,これまで画面に印字していたprintfも,プログラムを次のように起動すればファイルに書き出せる.

$ ./test > output.txt

もちろん入力と出力を同時に指定してもよい.

$ ./test < numbers.txt > output.txt

複数のファイルを同時に扱いたいとき以外は,いつも標準入力から読み込み,標準出力へ書き出せば(printfで書き出せば)よいのである.
Comments