scanf

関数scanfは、直感的でない挙動をすることがままあり、苦手とする人も多いだろう。 この文書では、scanfに関するエラーを起こさないような記法を学ぶ。

まず覚えてもらいたいのは、標準入力(stdin)というデータ領域である。 キーボードから受け取った入力はまずここに入り、それに対してアクセスする関数がscanf、getcharなどである。 注意しなければならないのは、これらの関数を使った後、標準入力が残ったままになる場合があることである。(代入に失敗したときなど)

関数の紹介

scanf:標準入力から、書式指定子の通りにデータを受け取る。 返り値はintで、代入に成功した変数がいくつあるかを返す。

    int num, x;
    x = scanf("%d", &num); //代入成功ならx==1

getchar:標準入力の先頭の1文字を受け取る。

    char ch;
    ch = getchar();

scanfの詳しい使い方

scanfは、一度に複数のデータを受け取ることができる。

    int num1, num2;
    scanf("%d%d", &num1, &num2);

このようにすると、改行、スペース、タブ文字を区切りとして、2つの整数を受け取ることができる。

    scanf("%d,%d", &num1, &num2);

この場合は、カンマ区切り。

書式指定子

scanfの第1引数には文字列が渡されるが、その中でデータの型を示す書式指定子が扱われる。 書式指定子には、以下の様なものが存在する。

  • %d -- int
  • %lf -- double
  • %c -- char
  • %s -- 文字列
  • %19s -- 文字列を19文字まで
  • %[abc] -- 標準入力をabc以外の文字が現れるまで読む
  • %[^\n] -- 標準入力を\n(改行)が現れるまで読む
  • %*c -- charを1文字読み飛ばす(代入抑止)

改行文字は見えないため、以下の様なミスはよく起こる。

    char ch1, ch2;
    scanf("%c", &ch1);
    scanf("%c", &ch2); //2回めの入力は受け取れない

これは、ch2に標準入力に残った'\n'が代入されるためである。 これを回避するためにはいくつか方法がある。

1.

    scanf("%c", &ch1);
    scanf(" %c", &ch2); //スペースは改行、スペース、タブ文字に対応する

2.

    scanf("%c%*c", &ch1); //代入抑止で'\n'を読み飛ばす
    scanf("%c", &ch2); 

3.

    scanf("%c", &ch1);
    getchar(); //改行文字を空読みする
    scanf("%c", &ch2); 

scanfと文字列

文字列はcharの連続データとして表現され、必ず一番最後の文字は'\0'(数値の0)である。よって、

    char str[20];

の様な場合は受け取れる文字数は19文字までとなる。

    scanf("%s", str); //20文字以上を代入しようとするとバグ
    scanf("%19s", str); //ok

また、この場合でも20文字以上を入力すると標準入力に文字が残る。 標準入力を空にしたい場合は、以下のようにするのが確実である。

    while(getchar() != '\n'); //改行文字が表れるまで空読み

標準入力でスペースを使うと、区切り文字として解釈される。

    char str[12];
    scanf("%11s", str);
    //入力:hello world
    //str == "hello"

文字列の中でスペースを使いたい場合は、以下のように記述する。

    char str[12];
    scanf("%11[^\n]", str); //改行以外を受け取る
    //入力:hello world
    //str == "hello world"

配慮した数値入力

ループを使って数値を入力させることを考える。

    while(scanf("%d", &num) != 1); //NG

このscanfは、代入に成功した場合1を返すので、間違った入力をはじくことができる。 しかし、このままでは代入に失敗した場合、標準入力に文字が残り、無限ループに陥る。 これを回避する場合は、先ほどの例を利用し、

    while(scanf("%d", &num) != 1)
    {
        while(getchar() != '\n');
    }

と書くとよい。