□scanf,fscanfは使わず fgets,sscanfのペアを使う


 入門書において、キーボードからの入力を行うのに、scanf()という関数を用いられることがあります。
 特定環境に依存せず、C言語の標準ライブラリの範囲で説明しようとすると(とくに文字列の説明より先に先にキー入力を行いたい場合は)、これを使わざる得ないでしょう。この関数は標準関数で(入門者の用いる)どのコンパイラにまずついていますし、かなり便利な関数です。
 ただし、この関数scanfや同類のfscanfは、少し問題があるため、実用プログラムに使われることはまずない、というのも現状です。
 最大文字数が指定できないという問題もあるのですが、他にも、たとえば
   n = scanf("%d %d", &a, &b);
 のように記述し、入力で
  1 2
 と入力するところを
  1
 のみ入力したところで改行をしてしまった場合、a = 1,b変わらず,n = 1のようにはならず、改行コードは空白と扱われるため次の数字が入力されるのを待ち続ける状態になります。
 これはよくも悪くもと思われますが、現実のエラーの発生パターンやエラー処理の都合からいうと 改行が空白として扱われるのは不便なことが多いのです。
 このため、現実的な処理では、scanf/fscanf を直接用いず、
  fgets(buf, sizeof(buf), fp);
  n = sscanf(buf, "%d %d", &a,&b);
  if (n != 2) printf("入力エラー\n");
のように、1行入力したのち、sscanfでそれを分解する、という方法を用いることになることが多いです。先に1行入力して確定しその範囲でデータの個数があってるかどうかでエラーチェックを行えるからです。 データ1個の入力でも、空入力などの(エラー)対策を施しやすいです。 なので人によっては scanf を教えず、はなっから fgets,sscanfの方法を教える場合もあるでしょう。
 ひょっとしたら、scanfは実用でないという話を聞いて、scanf系の関数すべてが駄目なんだと思いこんでる人がおられるかもしれませんが、 問題点は改行コードを空白として扱うことなので sscanfを用いない理由はないです (もちろん、sscanfの仕様が要求される仕様にあわない、 とかより細かなチェックが必要とかで、 同様の処理をする場合でもsscanfを用いないことは多々あります)。

 またキーボードから文字列や数値を入力するという動作は、実用プログラムで求められる仕様やエラー処理からすると標準入出力で行える範疇を超えるため結局 DOSに依存したり機種自体に依存したりWindowsに依存したりすることになり、利用者環境ごとに違ってくることになります。

 scanf は共通関数ゆえの入門時専用の関数くらいに思っておくのがよさそうです。 (もちろん欠点しってて使い捨てのテストプログラムなんかで用いることもあります)





やっぱりsscanfでも、あまり使わないかも...

 実は scanfを使うもんじゃないと聞いて scanf 系列すべてが駄目なんだと思いこんだのは自分自身だったんですが、気付いてからは手を抜けるときは sscanf を使うようにはなりました。 ただ、いまいち仕様を飲み込めてないせいか上手く利用できずにもいます。
たとえばデータをテキストファイル(csv形式)で
  name1,1,2
  name2,2,3
のように用意し
  fgets(buf, sizeof(buf), fp);
  n = sscanf(buf, "%s,%d,%d", name, &a, &b);
で読み込んでも期待したような動作にはなりません。データを
  name1 ,1,2
のように文字列の後ろに空白を入れればいいんですが……結局
  name1 1 2
のように空白区切りのデータでやるか、 あるいは文字列の長さ問題まで考えだすと("%10s"とかで済むときはいいだろうけど) 結局sscanfを使わず別の方法を使ってしまうことになります。
理解が足りてなくて使い方が悪いだけかもしれないけれど、やっぱり使い勝手悪いなあと思ってしまう(対になる? printfが使いよいから余計にそう思うだけかも)。