(Translated by https://www.hiragana.jp/)
scanf - Wikipedia

scanf(スキャンエフ)は、C言語げんご標準ひょうじゅん関数かんすうヘッダーファイル stdio.h定義ていぎされている、書式しょしき入力にゅうりょく関数かんすうである。

標準ひょうじゅん入力にゅうりょく大抵たいていキーボード)からの入力にゅうりょくを、書式しょしきしたがって変数へんすう機能きのうつ。標準ひょうじゅん出力しゅつりょく関数かんすうprintf対比たいひさせてかんがえるとかりやすい。

ユーザーからの入力にゅうりょくける、ごく基本きほんてき機能きのうつにもかかわらず、後述こうじゅつするように異常いじょう入力にゅうりょく(エラー)に配慮はいりょすると相応そうおう手間てまがかかるため、テストプログラムや入門にゅうもんしょのぞいてはあまり使つかわれない。

このファミリーの関数かんすうには、入力にゅうりょくストリームを指定していできる fscanf や、メモリじょう文字もじれつストリームを入力にゅうりょく対象たいしょうとする sscanf などがある。

形式けいしき

編集へんしゅう

stdio.hない以下いかよう宣言せんげんされている。

int scanf(const char *format, ...);

printf同様どうようだい1引数ひきすうformatは、それにつづ可変長かへんちょうじつ引数ひきすう変換へんかん方法ほうほう書式しょしき)を指定していする。またもど入力にゅうりょく(スキャン)に成功せいこうした入力にゅうりょく項目こうもくかずかえされる。

利用りようれい以下いかしめす。

#include <stdio.h>

int main(void)
{
  int x; /* スキャン結果けっか格納かくのうする変数へんすう。 */

  printf("x = ? "); /* 入力にゅうりょくうながすプロンプト。 */
  /* スキャンと結果けっか確認かくにん。 */
  if (scanf("%d", &x) == 1) {
    printf("スキャン結果けっか = %d\n", x);
  }
  else {
    printf("スキャン失敗しっぱい\n");
  }
  return 0;
}

変換へんかん指定してい

編集へんしゅう

scanf の変換へんかん指定していつぎ形式けいしきをとる。

% [代入だいにゅう抑止よくし][最大さいだいフィールドはば][なが修飾しゅうしょく]変換へんかん指定してい

代入だいにゅう抑止よくし

編集へんしゅう
フラグ 意味いみ
* フォーマットにわせて入力にゅうりょくむがじつ引数ひきすう代入だいにゅうはされない。

たとえば

char c;
scanf("%*c%c", &c);

というコードがあり、 "ab"という入力にゅうりょくがあった場合ばあい 1文字もじの 'a' は無視むしされ 2文字もじの 'b' という文字もじ代入だいにゅうされる。

なが修飾しゅうしょく

編集へんしゅう
修飾しゅうしょく 意味いみ 導入どうにゅうバージョン
hh じつ引数ひきすうは char がた C99以降いこう
h じつ引数ひきすうは short がた ぜんバージョン
l(エル) じつ引数ひきすうは long がたまたは wchar_t かたまたは double がた wchar_t についてはC95以降いこう
ll(エルエル) じつ引数ひきすうは long long がた C99以降いこう
j じつ引数ひきすうは intmax_t がた C99以降いこう
z じつ引数ひきすうは size_t がた C99以降いこう
t じつ引数ひきすうは ptrdiff_t がた C99以降いこう
L じつ引数ひきすうは long double がた ぜんバージョン

変換へんかん指定してい

編集へんしゅう
指定してい 意味いみ 導入どうにゅうバージョン
d,i 10しん符号ふごう整数せいすう ぜんバージョン
u 10しん符号ふごう整数せいすう ぜんバージョン
o 8しん符号ふごう整数せいすう ぜんバージョン
x,X 16しん符号ふごう整数せいすう ぜんバージョン
e,E 浮動ふどう小数点しょうすうてんすう ぜんバージョン
f,F 浮動ふどう小数点しょうすうてんすう ぜんバージョン
g,G 浮動ふどう小数点しょうすうてんすう ぜんバージョン
a,A 浮動ふどう小数点しょうすうてんすう C99以降いこう
c 文字もじ ぜんバージョン
s 文字もじれつ ぜんバージョン
p ポインタの対応たいおうする引数ひきすうは void* になる。 ぜんバージョン
n 整数せいすう変数へんすう出力しゅつりょく文字数もじすう格納かくのう ぜんバージョン
% '%'の入力にゅうりょく ぜんバージョン
[...] [ ]ないかこまれた文字もじだけを取得しゅとくし、
それ以外いがい文字もじあらわれた場所ばしょ以降いこう入力にゅうりょく終了しゅうりょうする(下記かき参照さんしょう)。
ぜんバージョン

[ ] はたとえば

char str[256];
scanf("%[abc]", str);

というコードがあり、入力にゅうりょくに "babaacdeabfghijabcef" という文字もじれつはいった場合ばあい、str には "babaac" という文字もじれつのみが入力にゅうりょくされ、のこりの文字もじれつ入力にゅうりょくされずに終了しゅうりょうする。strに代入だいにゅうされなかった、"deabfghijabcef"は入力にゅうりょくストリームにのこかたちとなる。また[^ ... ]とした場合ばあいぎゃくに[ ]ない文字もじはいってくるまで文字もじむ。 たとえば、

char str[256];
scanf("%[^abc]", str);

という場合ばあい、"ghetbceajk"と入力にゅうりょくすると、str には"ghet"が代入だいにゅうされる。上記じょうき同様どうように、入力にゅうりょくされなかった文字もじれつ入力にゅうりょくストリームに保持ほじされる。

コードれい

編集へんしゅう

ソース:

#include <stdio.h>
#include <limits.h>

int main(void)
{
  int n1, n2, nadd, nsub, nmul, ndiv, nmod;

  printf("1つ入力にゅうりょく数値すうち:");
  if (scanf("%d", &n1) != 1) {
    puts("スキャン失敗しっぱい");
    return -1;
  }
  printf("2つ入力にゅうりょく数値すうち:");
  if (scanf("%d", &n2) != 1) {
    puts("スキャン失敗しっぱい");
    return -1;
  }
  if (n2 == 0) {
    /* ゼロ除算じょざん防止ぼうしのチェック。 */
    puts("2つ数値すうちゼロを入力にゅうりょくしてください");
    return -1;
  }
  if (n1 == INT_MIN && n2 == -1) {
    /* 除算じょざん剰余じょうよのオーバーフローエラー防止ぼうしのチェック。 */
    puts("オーバーフローしない数値すうちわせを入力にゅうりょくしてください");
    return -1;
  }

  nadd = n1 + n2;
  nsub = n1 - n2;
  nmul = n1 * n2;
  ndiv = n1 / n2;
  nmod = n1 % n2;

  printf("%d + %d = %d\n", n1, n2, nadd);
  printf("%d - %d = %d\n", n1, n2, nsub);
  printf("%d * %d = %d\n", n1, n2, nmul);
  printf("%d / %d = %d + %d / %d\n", n1, n2, ndiv, nmod, n2);

  return 0;
}

出力しゅつりょく結果けっかれい:

1つ入力にゅうりょく数値すうち:60
2つ入力にゅうりょく数値すうち:21
60 + 21 = 81
60 - 21 = 39
60 * 21 = 1260
60 / 21 = 2 + 18 / 21

関連かんれんする関数かんすう

編集へんしゅう
int fscanf(FILE *fp, const char *format, ...);

fscanf はだい1引数ひきすうにFILEポインタを指定していすることで、標準ひょうじゅん入力にゅうりょくからではなくファイルストリームからむ。C言語げんごでは標準ひょうじゅん入力にゅうりょくは stdin というFILEポインタであらわすので、fscanf(stdin, ...) は scanf と完全かんぜん同義どうぎである。

int sscanf(const char *str, const char *format, ...);

sscanf はだい1引数ひきすう文字もじれつへのポインタを指定していすることで、標準ひょうじゅん入力にゅうりょくからではなく文字もじれつストリームからむ。後述こうじゅつするように scanf でエラー処理しょり実装じっそうしようとすると、エラーにならずに再度さいど入力にゅうりょくちになってしまうパターンがある。このため標準ひょうじゅん入力にゅうりょくから数値すうち入力にゅうりょくする場合ばあいには、直接ちょくせつ scanf を使つかわずにいったん fgets 関数かんすうとう文字もじれつとしてんでから sscanf で処理しょりすることのほうおおい。

scanfの問題もんだいてん回避かいひ方法ほうほう

編集へんしゅう

scanf は非常ひじょう簡潔かんけつ入力にゅうりょく制御せいぎょおこなえるため、C言語げんご入門にゅうもんしょではかならずとっていいほど登場とうじょうする。しかしこの関数かんすうにはいくつかの問題もんだいてん指摘してきされている。ここではよく指摘してきされる scanf の問題もんだいてんとその回避かいひ方法ほうほうについてせんべる。

改行かいぎょう文字もじあつか

編集へんしゅう

入力にゅうりょく文字もじ(char)の場合ばあいおもにキーボードで入力にゅうりょくさいされるリターンキーが無視むしできないという問題もんだいてんがある。たとえば

char a, b, c;
scanf("%c", &a);
scanf("%c", &b);
scanf("%c", &c);

とした場合ばあい通常つうじょうなら3かい入力にゅうりょくちがおこなわれることが期待きたいされているとおもわれるが、実際じっさいには2かいしかおこなわれない(予期よきしない入力にゅうりょくはここでは考慮こうりょしない)。これは最初さいしょの a の入力にゅうりょくには入力にゅうりょくされた文字もじ代入だいにゅうされるが、このときストリームうえ改行かいぎょうコードがのこされてしまい、つぎの b には a を入力にゅうりょくするさい押下おうかされたリターンキーの改行かいぎょうコードが代入だいにゅうされるためである。通常つうじょうの %d や %s の場合ばあい改行かいぎょうコードは無視むしして入力にゅうりょくむので問題もんだいにはならないが、 %c の場合ばあい無条件むじょうけんにストリームじょうつぎのバイトをかえすためこのような現象げんしょう発生はっせいする。これをふせぐには、

scanf("%c", &a);
scanf(" %c", &b);
scanf(" %c", &c);

のように最初さいしょ空白くうはくれることで回避かいひされる。これは入力にゅうりょくまえ空白くうはく改行かいぎょうコードもふくむ)をばすことを意味いみしている。ただしこの場合ばあい a, b, c に半角はんかくスペースを代入だいにゅうすることはできない。そのような場合ばあいとしては

scanf("%c%*c", &a);
scanf("%c%*c", &b);
scanf("%c%*c", &c);

という方法ほうほうがとられる。これは入力にゅうりょくしたさい改行かいぎょう文字もじを%*cで除去じょきょすることを意味いみする。

空白くうはく、タブのばし

編集へんしゅう

scanf は空白くうはくとタブは改行かいぎょう文字もじおな区切くぎ文字もじとしてあつかわれる。たとえば、

char a[20];
scanf("%s", a);

という方法ほうほう文字もじれつもうとしたとする。このとき入力にゅうりょく文字もじれつが "This is a pen" というものだった場合ばあいじつ引数ひきすう入力にゅうりょくされるものは "This" までである。これだけでも問題もんだいであるが、さらに問題もんだいなのは、のこりの " is a pen" という文字もじれつはストリームじょうのこされてしまうことである。このためつぎに scanf をんだ場合ばあい入力にゅうりょくにかかわらずじつ引数ひきすうに "is" という文字もじれつ代入だいにゅうしようとしてしまうことになる。これをふせ手段しゅだんとしては

scanf("%[^\n]", a);

という方法ほうほうがとられる。この場合ばあい改行かいぎょう文字もじ以外いがい文字もじれつすべて a に代入だいにゅうするため、a には空白くうはくやタブもふくめて文字もじれつ代入だいにゅうされる。なお前述ぜんじゅつにおける入力にゅうりょく改行かいぎょうコードのあつかいも考慮こうりょした場合ばあいは、

scanf("%[^\n]%*c", a);

という記述きじゅつになる。

バッファオーバーラン

編集へんしゅう

の C言語げんご文字もじれつ処理しょり関数かんすう同様どうように、scanf を使用しようするさいにもバッファオーバーラン発生はっせいする可能かのうせいがある[1]たとえば

char a[20];
scanf("%s", a);

というコードがあった場合ばあい、a に入力にゅうりょくできる文字もじれつ終了しゅうりょう文字もじのぞく、19バイトまでしか入力にゅうりょくすることが出来できず、それ以上いじょう文字もじれつ入力にゅうりょくされるとバッファオーバーランが発生はっせいする。a の領域りょういき十分じゅうぶんであれば問題もんだいはないが、しかし入力にゅうりょくされてくる文字もじれつながさを予測よそくすることは不可能ふかのうであり、結局けっきょく a のながさをいくらおおきくしても、さらにそれを上回うわまわながさの入力にゅうりょくおこなわれてしまえばバッファオーバーランが発生はっせいしてしまう危険きけんせいがある。 この問題もんだいふせ手段しゅだんとして一般いっぱんてきなのは最大さいだいフィールドはば指定していすることである。上記じょうきれい場合ばあい

char a[20];
scanf("%19s", a);

とすることで、aには最初さいしょの19バイトがまれ入力にゅうりょく終了しゅうりょうする。ただしこの場合ばあいのこりの文字もじれつはストリームじょうのこってしまうので実際じっさいには以下いかのようにすることで対処たいしょされる。

char a[20];
scanf("%19s%*[^\n]", a);

これは19バイトみさらに改行かいぎょう文字もじまでの文字もじれつそらみすることを意味いみする。

さらに前述ぜんじゅつ改行かいぎょうコードがストリームにのこ問題もんだい考慮こうりょすると

char a[20];
scanf("%19s%*[^\n]%*c", a);

となるが、この場合ばあいはaにはい文字もじれつが 19バイト以下いか場合ばあいには、入力にゅうりょくストリームにやはり改行かいぎょう文字もじのこる。よって実際じっさいには、

char a[20];
scanf("%19s%*[^\n]", a);
getchar();

のように入力にゅうりょく終了しゅうりょう改行かいぎょう文字もじそらみするなどの方法ほうほうがとられる。空白くうはく文字もじあつかいも考慮こうりょする場合ばあい

char a[20];
scanf("%19[^\n]%*[^\n]", a);
getchar();

となる。

バージョン8.0 (2005) 以降いこうMicrosoft Visual C++コンパイラには、scanf_s() という関数かんすう用意よういされていて、バッファのサイズを指定していすることができる。scanf_s はC11規格きかく標準ひょうじゅんされたが、実装じっそう任意にんいである。

異常いじょう入力にゅうりょくおこなわれたとき処理しょり

編集へんしゅう

scanf関数かんすうは、予期よきせぬ入力にゅうりょくされると、そのまず、ストリームうえのこしてしまう。たとえば

int i;
scanf("%d", &i);

としたコードがあった場合ばあい入力にゅうりょく数字すうじでなかった場合ばあいは scanf はストリームにその文字もじれつのこしたまま終了しゅうりょうすることになる。このためつぎに scanf がばれたときはそのストリームじょうのバッファがじつ引数ひきすう代入だいにゅうされ、の scanf にも異常いじょう結果けっかかえすことになる。れいとして 1 が入力にゅうりょくされたら終了しゅうりょう、それ以外いがいはもう一度いちど入力にゅうりょくもとめるようなプログラムをかんがえてみる、もしこのプログラムを

int i;
while(1) {
  scanf("%d", &i);
  if (i == 1) {
     break;
  }
}

というようなコードで記述きじゅつした場合ばあい数字すうじ以外いがい文字もじ入力にゅうりょくしてしまうと無限むげんループおちいることになる。このような現象げんしょうふせ手段しゅだんとして scanf のもど利用りようする方法ほうほうがとられる。これは scanf は代入だいにゅう成功せいこうした変数へんすうかずもどとしてかえすため、指定していしたじつ引数ひきすうかずと scanf のもど一致いっちしないときに入力にゅうりょくバッファをクリアすることで回避かいひできる。たとえば上記じょうきれい場合ばあい以下いかのようになる。

int i;
while(1) { 
  if (scanf("%d", &i) != 1) {
     scanf("%*s");
     if (feof(stdin)) {
       /* エラー処理しょり */
     }
     continue;
  }

  if (i == 1) {
     break;
  }
}

入力にゅうりょくバッファのクリアはここでは文字もじれつそらみすることで実現じつげんしている(入力にゅうりょくバッファのクリア方法ほうほう状況じょうきょうによってことなる方法ほうほうかんがえられる)。なおおもにウェブじょうなどで、標準ひょうじゅん入力にゅうりょくのバッファクリアの方法ほうほうに fflush(stdin) や rewind(stdin) とする方法ほうほう紹介しょうかいされることがあるが、ANSI規格きかくではこれらの動作どうさ未定義みていぎであり、ただしい動作どうさ保障ほしょうするものではない。

この方法ほうほうでエラー処理しょりをする場合ばあいには上記じょうき空白くうはく文字もじあつかいの関連かんれん問題もんだいになることがある。たとえば

int i, j;
scanf("%d %d", &i, &j);

のようなコードで、異常いじょう入力にゅうりょくがあった場合ばあいエラー処理しょりをしたいとする。これを

int i, j;
if (scanf("%d %d", &i, &j) != 2) {
  エラー処理しょり
}

という方法ほうほう実現じつげんすると入力にゅうりょくが "1" のみの場合ばあい空白くうはく改行かいぎょう同一どういつされてしまうため、変数へんすう "i" に 1 を入力にゅうりょくして変数へんすう "j"の入力にゅうりょく状態じょうたいとなる。もし、このような場合ばあいにもエラー処理しょり実行じっこうしたい場合ばあいには、たとえば以下いかのように一度いちど文字もじれつとしてんで、その sscanf でむことで実装じっそうすればよい。

char a[20];
int i, j;

scanf("%19[^\n]%*[^\n]", a);
getchar();

if (sscanf(a, "%d %d", &i, &j) != 2) {
  エラー処理しょり
}

脚注きゃくちゅう

編集へんしゅう
  1. ^ [迷信めいしん] scanf ではバッファオーバーランをふせげない”. C/C++迷信めいしんしゅう. 株式会社かぶしきがいしゃきじねこ. 2010ねん2がつ28にち閲覧えつらん。 “書式しょしき指定してい不適切ふてきせつなために発生はっせいする脆弱ぜいじゃくせいであって、scanf の問題もんだいではありません。”

関連かんれん項目こうもく

編集へんしゅう