scanf関数
scanf
関数は標準入力から文字列を受け取り、適切な形式に変換して変数に格納する関数です。
scanf
関数はprintf
関数と対になる関数で、C言語の基本的な関数であるのに非常に扱いが難しい関数です。
scanf
関数の第一引数は書式指定文字列という特殊な文字列を指定します。
書式指定文字列は、scanf
関数の第二引数以降に指定した変数にどのような値を格納するかを指定します。
int num = 0;
scanf("%d%*c", &num);
printf("今入力した値:%d", num);
123
今入力した値:123
このコードの第一引数の文字列中に%d
という文字列が存在します。
この%d
は第二引数の変数num
にユーザーが入力した数値を取り込む、という意味を持ちます。
scanf
関数は入力された文字列中の半角の空白を区切り文字として複数の変数に値を格納します。
int num1 = 0, num2 = 0;
scanf("%d%d%*c", &num1, &num2);
printf("今入力した値:%d, %d", num1, num2);
123 456
今入力した値:123, 456
変換指定子に%c
を指定すると空白文字も読み取られます。
このページではscanf関数の書式指定文字列の詳細な使用方法を解説します。
scanf関数の類似関数であるfscanf
関数、sscanf
関数、vscanf
関数などの書式指定文字列も同様の使用方法です。
scanf_s関数
scanf
関数にはセキュリティ強化版のscanf_s
関数があります。
scanf_s
関数はいくつかの書式指定文字列を指定した場合に、入力対象の引数の次の引数に入力されるデータのサイズを指定する必要があります。
読み取られるデータサイズが制限されるためセキュリティが向上します。
char str[8];
scanf_s("%7s", str, 8);
printf("今入力した値:%s", str);
123456789
今入力した値:1234567
上記コードは入力ストリームにデータが残っていることを考慮する必要があります。
Visual Studioでは標準でscanf
関数は使用禁止となっています。
scanf
関数を使用するにはSDLチェックを無効にする必要があります。
(_s系関数とエラー表示について)
書式指定文字列
書式指定文字列は変換指定子をひとつ以上含む文字列です。
変換指定子は%
記号から始まります。
書式指定は以下の説明に登場する順序で指定します。
変換指定子以外の書式指定は省略可能です。
代入抑止文字
代入抑止文字は指定された通りの読み取りは行われますが、引数への格納は行われません。
つまり入力は読み捨てられます。
代入抑止文字は*
(アスタリスク)です。
これは省略可能です。
int num = 0;
scanf("%*d", &num);
//numには何も入力されない
最大フィールド幅
最大フィールド幅は変数に格納される最大の文字数を指定します。
最大フィールド幅以上の入力があった場合、読み取れなかった分は標準入力にデータが残ります。
最大フィールド幅は1以上の整数で指定します。
これは省略可能です。
char str[16];
scanf("%15s", &str);
//strには15文字以上入力されない
長さ修飾子
長さ修飾子は実引数のデータ型(サイズ)を指定します。
長さ修飾子は省略可能です。
長さ修飾子 | 変換指定子 | データ型 |
---|---|---|
hh | d、i、o、u、x、X、n | char型、unsigned char型 |
h | d、i、o、u、x、X、n | short型、unsigned short型 |
s、S | 1バイト文字列 | |
l(小文字のL) | d、i、o、u、x、X、n | long型、unsigned long型 |
e、E、f、F、g、G | double型 | |
s、S | ワイド文字列 | |
ll | d、i、o、u、x、X、n | long long型 |
L | e、E、f、F、g、G | long double型 |
以下はMicrosoft独自の拡張です。
長さ修飾子 | 変換指定子 | データ型 |
---|---|---|
h | c、C | 1バイト文字 |
l(小文字のL) | c、C | ワイド文字 |
I64 | d、i、o、u、x、X | int64型 |
変換指定子
変換指定子 | 引数の型 | 説明 | scanf_s関数でのサイズ指定 |
---|---|---|---|
d | 整数へのポインタ | 10進整数 | 指定しない |
i | 整数へのポインタ | 整数 0xから始まる場合は16進数 0から始まる場合は8進数 それ以外は10進数 |
指定しない |
o | 符号なし整数へのポインタ | 8進整数 | 指定しない |
u | 符号なし整数へのポインタ | 10進整数 | 指定しない |
x、X | 符号なし整数へのポインタ | 16進整数 | 指定しない |
e、E、f、F、g、G | 小数へのポインタ | 小数 | 指定しない |
c | char型へのポインタ | 文字 空白文字を区切り文字としない (空白も読み取る) 最大フィールド幅に2以上を指定することで配列への格納も可能 その際に終端にNULL文字は付加しない |
保存先領域のサイズ(文字数)を指定 最大フィールド幅と同時に指定する |
s | char型へのポインタ | 文字列 終端にNULL文字を付加する 最大フィールド幅は保存先領域サイズより1少ない値を指定する |
保存先領域のサイズ(文字数)を指定 最大フィールド幅と同時に指定する |
[] | char型へのポインタ | 文字種の指定 []内に読み取る文字を指定する [abc]を指定すればaまたはbまたはcが入力された場合に読み取る [^a]を指定すればa以外が入力された場合に読み取る [0-9]などの範囲指定が可能 終端にNULL文字を付加する 最大フィールド幅は保存先領域サイズより1少ない値を指定する |
保存先領域のサイズ(文字数)を指定 最大フィールド幅と同時に指定する |
p | void**型 | ポインタ | 指定しない |
n | 整数へのポインタ | 入力の読み取りは行わない 現時点までに入力された文字数を整数へのポインタに格納する |
指定しない |
% | %の入力 | 指定しない |
戻り値
scanf
関数の戻り値は入力された項目の数を返します。
エラーが発生した場合はEOFを返します。
サンプルコード
入力ストリームのクリア
scanf
関数などの入力関数は不正な入力が発生した場合に処理を停止することがあります。
その場合、変数にデータが保存されないのはもちろんですが、読み取られなかったデータが入力ストリームに残ることがあります。
また、想定以上のデータが入力された場合も読み取れなかったデータはストリームに残ります。
これを考慮せずに再度入力関数を実行すると、大抵は意図しない動作となります。
scanf
関数では以下の記述で入力ストリームのデータをクリアすることができます。
scanf("%*[^\n]");
scanf("%*c");
//二行目はgetcharでも良い
[^\n]
は「\n以外」、つまり改行文字以外の文字全てを意味します。
これに%*
を指定しているため、これは「改行文字以外の文字を読み捨てる」という意味になります。
上記の変換指定により、ストリームは最後の改行文字直前まで読み捨てられた状態になります。
次の%*c
で、最後の改行文字を読み捨てています。
これで入力ストリーム上のデータはすべて読み取られた状態となります。
データはすべて読み捨てるのでscanf
関数の第二引数は必要ありません。
これを以下のようにひとつにまとめてしまうとうまく動作しない場合があります。
scanf("%*[^\n]%*c");
この記述は、ストリームに改行文字のみが残っている状態で実行すると最初の%*[^\n]
で何も読み取られません。
すると続く%*c
も実行されないため、ストリームがクリアできません。
%*[^\n]
が実行されようとされまいと改行文字を読み捨てるために二行に分離する必要があります。
ストリームのクリアは何らかの入力を行った直後に常に記述し、ストリームは常に空にしておくことをおすすめします。
ストリームが空かどうか分からない状態で確実にストリームを空にする方法が存在しないためです。
ストリームが空の状態で上記のクリア処理を行うと不要な入力待ちが発生します。
(致命的なエラーになるわけではありませんが)
fflush(stdin)
やrewind(stdin)
などで入力ストリームをクリアしているコードがありますが、これらを標準入力(stdin)に使用した場合の動作は未定義なので推奨されません。
%cで空白を受け取らない
%c
は文字をひとつ読み取りますが、空白や終端の改行文字も読み取ります。
(%d
などは空白や改行文字は無視されます)
これらの文字が必要ない場合は直前に空白を入力することで読み捨てることができます。
char c1, c2;
printf("文字をふたつ入力してください\n");
scanf(" %c", &c1);
scanf(" %c", &c2);
//入力ストリームのクリア
scanf("%*[^\n]");
scanf("%*c");
printf("今入力した文字: %c, %c", c1, c2);
getchar();
空白を含む文字列の読み取り
%s
は文字列を読み取りますが、これは空白の直前までしか読み取ることができません。
英文などの空白を含む文字列を行末まで読み取るには以下のようにします。
char str[32];
scanf("%31[^\n]", str);
//入力ストリームのクリア
scanf("%*[^\n]");
scanf("%*c");
printf("今入力した文字列:\n%s", str);
getchar();
%s
ではなく%[^\n]
とすることで、改行文字までの文字列を空白も含めて読み取ることができます。
そのままではバッファオーバーランが起こる可能性がありますから、最小フィールド幅を指定して入力文字数を制限します。
(これは%s
指定する場合でも同様です)
入力チェック
ユーザーは常に適切なデータを入力してくれるとは限らないので入力チェックは必須です。
scanf
関数の戻り値は入力に成功した変数の数ですから、これで入力チェックを行います。
int num1, num2;
while (1)
{
printf("数値をふたつ入力してください。\n");
int count = scanf("%d%d", &num1, &num2);
//ストリームのクリア
scanf("%*[^\n]");
scanf("%*c");
if (count == 2)
break;
printf("入力エラー。\n\n");
}
printf("入力された数値:\n%d, %d", num1, num2);
getchar();
入力エラーがあった場合、入力データがストリームに残ったままになる場合があります。
そのままループさせると内容によっては無限ループに陥りますから、ストリームをクリアしておきます。
参考