2012/05/26

C言語 入力関数 scanf, fgets

入門書で、まず紹介されているキーボードからの書式付入力 scanf。出力の printf の逆であり使い方も似ている。実際のプログラムでは、scanf は予期せぬ入力に対応しきれいないため、あまり使われないらしい。入力関数は scanf 以外にも fscanf や gets などがあるが、これらも推奨ではないようだ。getsに至っては最新のC11では廃止された模様。では推奨は何?という疑問だが、それぞれ問題があって対処しながら使うというのが現実のようだ。それでも fgets が使いやすいようだ。fgets は本来ファイルから文字列を読み取るための関数だが、キーボードからの入力もファイルと同じように扱うことができる。とりあえず入力は fgets だけ使っていればよいみたい。このページでは、まず scanf を試してみる。そして fgets でも同じことをしてみようと思う。

scanfのシンプルな例

#include <stdio.h>
int main(void){
  char str[10];
  printf("文字列入力\n");
  scanf("%s", str);
  printf("%s\n", str);
  return 0;
}
入力した文字列(char 10バイト)をそのままコマンドプロンプトに出力するプログラム。例えば「123abcあいうえお」と入力すると、「123abcあいうえお」と出力される。文字列の配列の10バイトを超えているが、ポインタで扱われているので、入力した文字列はすべて表示される。注意点はスペース、タブ、改行などが入ると、そこまでしか読み取らない。
文字列入力
123abcあいうえお
123abcあいうえお
続いてfgetsで同じ内容を書いてみる。

fgetsのシンプルな例

#include <stdio.h>
int main(void){
  char str[10];
  printf("文字列入力\n");
  fgets(str,10,stdin);
  printf("%s\n", str);
  return 0;
}
fgetsでは1行、もしくは指定した「バイト数-1」だけ読み取る。最後には「\0」が自動で入るので用意した配列の1バイト分を消費する。「123abcあいうえお」と入力すると「123abcあ\202」となり、「い」の2バイト分のうち半分しか読み取らない。一見使いにくそうだが、指定どおり動くことで安全なプログラムが組める。またスペースなどが入っていても、そのまま読み取ることができるのが最大のメリット。
文字列入力
123abcあいうえお
123abcあ\202

実行順の問題

上記の入力関数は、Windowsのコマンドプロンプトではうまく行くが、Meadow上からshell(コマンドプロンプト)を起動させて実行すると、プログラムの printf("文字列入力\n"); よりも入力関数のscanfやfgetsが先に実行されてしまう。下の例のようにバッファを使って回避することは可能。
#include <stdio.h>
int main(void){
  char str[10];
  char buf[512];
  printf("文字列入力\n");

  setbuf(stdout,buf);
  fgets(str,10,stdin);
  fflush(stdout);

  printf("%s\n", str);
  return 0;
}
setbuf()を使って fgets の実行を遅らせ、fflush()で fgets を実行させている。この処理を加えることで、fgets が先に実行されないようになる。バッファサイズはいろいろ試して、この場合512にしている。これよりも小さいとエラーになりやすかった。

C言語 ANSI C89 Meadow & MinGW GCC 目次はこちら