補足10:scanf を使わないコンソール入力


scanf は「使うな」とか「初心者には教えるな」とか、 いろいろ評判の悪い関数ですが、 仕様さえきちっと守って使えば(それが難しい関数なんですが) それなりに便利に使えます。 しかし、無理に scanf を使ってトラブルを抱え込むよりは、 それを使わないというのも一つの見識です。 scanf の代替案としては、fgets を使って入力の1行全体を読み込み、 それを分解して使うという方法がよく用いられます。

char inputline[10];

fgets(inputline, sizeof inputline, stdin);

fgets によって標準入力(キーボード)から入力した文字列が、 最後の改行を含めて char 型の配列 inputline に格納されます。 入力した文字列が 9 文字(10 文字 - 1 文字)より長ければ、 10文字目(imputline の最後の要素)に '\0' を代入して、 入力の残りは次の入力に回されます。

abcde[改行]

という入力であれば、inputline には

"abcde\n"

という文字列が格納され、

abcdefghijklmno[改行]

という入力であれば、

"abcdefghi"

という文字列が格納され、残りの "jklmno\n" は次に fgets のような入力関数が実行されたときに読み込まれます。

sizeof inputline は配列 inputline のサイズを求めています。 stdin は standard in、すなわち標準入力を示すファイルポインタです。

これを数値に変換するには atoi などの関数を用います。 これらの関数を利用するには、stdlib.h を include しておく必要があります。

#include <stdio.h>
#include <stdlib.h>

void func(void)
{
    char inputline[100];

    if (fgets(inputline, sizeof inputline, stdin) != NULL) {
        int i;
        long l;
        unsigned long u;
        double d;

        i = atoi(inputline);          /* int に変換           */
        l = strtol(inputline, NULL);  /* long に変換          */
        u = strtoul(inputline, NULL); /* unsigned long に変換 */
        d = atof(inputline);          /* double に変換        */
        d = strtod(inputline, NULL);  /* double に変換        */
    }

    ...
}

改行ごとにひとつの数値しか入力しないのであれば上のような方法で十分です。 1行で複数の数値を入力する場合は、それを切り分ける必要があります。 それには strtok が便利です。 strtok を使うには、string.h を #include しておきます。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void func(void)
{
    char inputline[100];
    int i[10], n = 0;

    if (fgets(inputline, sizeof inputline, stdin) != NULL) {
        static char separator[] = " \t\n";
        char *token;

        if ((token = strtok(inputline, separator)) != NULL) {
            do {
                i[n] = atoi(token);
            }
            while (++n < 10 && (token = strtok(NULL, separator)) != NULL);
        }
    }

    ...
}

strtok は第2引数に指定した文字列をセパレータにして 第1引数に指定した文字列を分解し、 その最初の文字列(トークン)へのポインタを戻り値として返します。 第1引数に NULL を指定すると、 以前に strtok を呼び出したときの文字列の残りの部分からトークンを切り出します。 第2引数にはコンマ、スペース、タブ、改行をセパレータに指定しており、 区切り文字はこのどれでもかまいません。

もう一つ、sscanf という関数を用いることもできます。 この動作は scanf と同じですが、 標準入力ではなく文字列を対象にします。

#include <stdio.h>

void func(void)
{
    char inputline[100];
    double x, y, z;

    if (fgets(inputline, sizeof inputline, stdin) != NULL) {
        sscanf(inputline, "%lf %lf %lf", &x, &y, &z);

        ...
    }
}

第1引数に変換する文字列を指定します。 第2引数以降は scanf の第1引数以降と同じです。