みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

scanf()で、エラー対応

scanf()を使用して、入力で例えば「5462fa」数字ではなく文字を入力してしまった場合エラー(無限ループ)になりますが、
これをscanf()を使用したまま再入力を促すことが可能でしょうか?よろしくお願いします。

#include <stdio.h>

int main(){
  int a , kazu;

  for(a=0;a<1;){
    printf("値入力せよー>");
      scanf ("%d", &kazu);
        if( kazu >= 1 && kazu <=100 ){
          a = a + 1 ;
        }else{
          printf("1から100で入力せよ\n");
        }
  }
  printf ("kazu = %d", kazu);
  return 0;
}

投稿日時 - 2006-03-29 11:59:30

QNo.2058639

すぐに回答ほしいです

質問者が選んだベストアンサー

がるです。なるほど、独学でなさっているのですね。
scanfの経緯は…もう一つ不明ですが。前述したとおり、C言語の関数には「使ってはいけない」ものが色々と転がっております(苦笑
scanfは「意図しない文字列によって容易に暴走する」「バッファにデータを残しっぱなしにすることがある」など、問題児として筆頭に上げられてしまうような関数です。
あと、入力周りでNGなのがgetsという関数。これは、引数のバッファサイズ指定が無いために、容易にバッファオーバフロウを引き起こします。
多少手間でも、fgetsを用いるべきです。

こういった関数の見分け方としては
・異常な入力に対してどのような挙動をしめすか
が第一点(scanfはここでNG)。
もう一つは
・引数で渡したポインタにデータを格納する場合、そのバッファサイズが引数として付随しているか
が第二点目になります(getsはここでNG)。

大変だと思うのですが、頑張ってください。

投稿日時 - 2006-03-29 15:02:53

お礼

回答ありがとうございます。
丁寧に解説していただき、助かりました。

投稿日時 - 2006-04-08 23:56:32

ANo.6

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(7)

do
{

}while()

のようなdo文で意図する数値が入力されるまで「1から100で入力せよ」と警告を出すプログラムにすればいいのではないでしょうか?

投稿日時 - 2006-04-01 03:28:59

お礼

回答ありがとうございました。

投稿日時 - 2006-04-08 23:55:46

ANo.5

がると申します。
・scanfの類は一切使わない
これに尽きます。
少なくとも、業務ないしそれに近しいところでscanf使ってたら問答無用で雷落とします。

っつか、何ゆえにあんな物騒な関数つかうのでしょう?
# 「課題で使えと書いてある」とか言われると言い返せないのですが。…無論「そんな戯けた問題だす教師」には限りなく疑問です。

C言語にはscanf以外にも「絶対に使ってはいけない」関数が複数あります。
そういった部分について学ばれることを強くお勧めいたします。

投稿日時 - 2006-03-29 14:26:03

お礼

ご指導ありがとうございます。
業務では使わないのですか、学校レベルの勉強用関数ってところですかね。
独学で勉強中なので、結構つらいです。また、こちらで質問させていただきます。ありがとうございました

投稿日時 - 2006-03-29 14:32:19

ANo.4

scanf(3)は、対話入力に不適当な関数です。
想定外の入力に対する挙動が、人間にはとても理解しにくい仕様になっています。
sscanf(3)で書き換えたほうがよいです。
-- 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- 8< -- 8< --
#include <stdio.h>

int
main()
{
char buf[BUFSIZ], moji[BUFSIZ];
int n, kazu;

while (1) {
printf("値入力せよー> ");
fgets(buf, BUFSIZ, stdin);
n = sscanf(buf, "%d%s", &kazu, moji);
if (kazu >= 1 && kazu <= 100 && n == 1) {
break;
}
else {
printf("1から100で入力せよ\n");
}
}
printf("kazu = %d\n", kazu);
return 0;
}

投稿日時 - 2006-03-29 12:53:34

ANo.3

>printf("1から100で入力せよ\n");
の前後あたりに
fflush(stdin);
をいれてやるとか

投稿日時 - 2006-03-29 12:49:17

ANo.2

まず第1に、scanfの返り値がvoidでなくintになってる理由を考えてみましょう。

第2に、scanfの返り値が「想定外」だった時の処理も含めてループの終了条件を考えましょう。

第3に、scanfは「書式に合わない入力があると、スキャンをその時点で中断し、ストリームに入力を残す」と言う特性があるので、ストリームに入力を残さない方法を考えましょう。

なお「正しい値が来るまで繰り返し」と言う処理は

  for (;;) {
    入力を促す画面出力
    入力処理
    if文で入力処理が行われたか判定 {
      異常入力の警告出力
      異常入力を除去するなどのエラー後処理
      continue;
    }
    if文で入力値の範囲の判定 {
      範囲外入力の警告出力
      continue;
    }
    if文でその他の再入力の判定1 {
      再入力の警告出力1
      continue;
    }
    if文でその他の再入力の判定2 {
      再入力の警告出力2
      continue;
    }
    break; /* 正常ならループ脱出 */
  }

のように書きます。

こうすれば「再入力の判定」は幾つでも書く事が出来ます。

どの再入力判定にも引っ掛からなかった場合のみ正常に入力されたと考え、ループの最終位置で無条件にbreak文でループを抜けます。

また、質問文にあるような「終了フラグを判定するループ文」だと
・終了フラグを初期化する必要がある
・ループ終了の為の判定を行い、終了フラグを立てる
・ループ終了フラグを判定してループを繰り返すか決める
と言うように、1つの条件に対してif文とfor文の2ヶ所で判定が行われ、二度手間を行っていて処理が無駄です。

こういう場合は
・終了フラグは使わない
・判定式の無い無限ループを使う
・ループ終了と判定したら、そのif文の中で(後処理をしてから)break文を使ってループを終らせる
・ループ再開(やり直し)が必要と判定したら、そのif文の中で(後処理をしてから)continue文を使ってループの最初に戻る
と言う書き方をしましょう。

投稿日時 - 2006-03-29 12:48:15

ANo.1

どうしたいのかにもよりますけど,
scanf("%*[^\n]");
とかでごまかせない?
あと, scanf の返り値はチェックすること.

投稿日時 - 2006-03-29 12:25:51

補足

回答ありがとうございます。
返り値のチェックはどのようにするのですか?

投稿日時 - 2006-03-29 12:40:26