next up previous contents
Next: B.14 ポインターについてどれだけ知ればいいですか? Up: B. がらくた箱 Previous: B.12.0.0.1 なぜ正の添字からは始められないか?:

B.13 scanf() を使うのは邪道だと言われちゃった

そもそも scanf というのは、お節介と言ってもいいくらい、複雑な処理を することが出来る関数なんです。そういう奴が間違った入力を受けとると、ず いぶんと間抜けなことをしてくれます。 scanf() は正常に読みとれた値の個 数を返しますから、それをチェックすればいいと考える人もいるでしょうが、 実は駄目です。

/* とりあえず正しいつもりのプログラム */
#include <stdio.h>

int main()
{
    double a;
    while (1) {
        if (scanf("%lf", &a) == 0) {
            fprintf(stderr, " scanf() で 0 個しか正常に読み込めませんでした。\n");
        }
        else {
            printf("%f\n", a);
            exit(0);
        }
    }
    return 0;
}
これを実行すると以下のようになります。

oyabun%	test-scanf
1.2
1.200000
oyabun%	test-scanf
0.000000
oyabun%	scanf
1,2
1.000000
oyabun%	scanf
,2
 scanf() で 0 個しか正常に読み込めませんでした。
 scanf() で 0 個しか正常に読み込めませんでした。
 scanf() で 0 個しか正常に読み込めませんでした。
略
 scanf() で 0 個しか正常に読み込めませんでした。
 scanf() で 0 個しか正常に読み込めませんでした。
^C scanf() で 0 個しか正常に読み込めませんでした。

oyabun%
つまり ``,'' という読み込めない文字があると、ポインターを先に進 めずに終了するため、「引っかかってしまって」無限ループに落ちるわけです。 このような欠点がありますから、システム・プログラム等で scanf() を使う のは確かに馬鹿者です。

では正しい入力はというと、

  1. とりあえず fgets() で文字列として読み込む。
  2. 読み込んだ文字列を次のいずれかで解析する。
    1. sscanf() 個数チェックが出来る。
    2. atof() 不正入力と 0 入力が区別出来ない。
    3. strtod() どこまで解析出来たかの位置まで分かる(ほぼ完璧なチェッ ク)。
として行なうことになります。


#include <stdio.h>
#include <stdlib.h>             /* atof() のため */
#include <floatingpoint.h>        /* strtod() */

int main()
{
    double a;
    char input[100];
    if (fgets(input, sizeof(input), stdin) == NULL) {
        fprintf(stderr, "input error?\n");
    exit(1);
    }
    /* atof() の利用 */
    a = atof(input);
    printf("%f\n", a);
    /* sscanf() の利用 */
    if (sscanf(input, "%lf", &a) == 0) {
        fprintf(stderr, " sscanf() で 0 個しか正常に読み込めませんでした。\n");
    }
    else
        printf("%f\n", a);
    /* strtod() の利用 */
    {
    char *p;
    a = strtod(input, &p);
    if (input == p) {
        fprintf(stderr, " strtod() が読み込みに失敗しました\n");
        exit(1);
    }
    printf("%f\n", a);
    }
    return 0;
}

どうですか?めんどい、やってらんない、と思う人が多いと想像します。筆 者は、普通の数値計算実行者が作る、いわゆるシステム・プログラムでないプ ログラムでは、それほど神経質になる必要はなく、「あ、入力を間違えた!し かたないから、最初からやりなおそう」でいいと思います。ただし、長い計算 の後にユーザーのキーボードからの指示を読んで態度を決めるようなプログラ ムでは、それまでの計算結果を無駄にしないように、それなりの注意がいるで しょう。その場合も sscanf() でチェックする程度でよいと思います。


next up previous contents
Next: B.14 ポインターについてどれだけ知ればいいですか? Up: B. がらくた箱 Previous: B.12.0.0.1 なぜ正の添字からは始められないか?:
Masashi Katsurada
平成18年4月28日