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

解決済みの質問

数値連続入力プログラムでの配列に格納される文字について

--------------------------------------------------------
#include<stdio.h>
int main(void)
{
   double sum=0.0;
   double dt,x
   int ret,n;
   char ss[80];

   ret=scanf("%lf",&dt);

   while(1){
      if(ret==1){
         x=sum;
         sum+=dt;
         n=getchar();
        if(n=='\n'){
           printf("入力された数値=%f\n");
           puts("");
           sum=sum;
        }
        else{
         printf("正しく入力してください\n");
         puts("");
         gets(ss);
         sum=x;
         dt=0.0;
        }
      }

      else if(ret!=0){
         gets(ss);
         dt=0.0;
         printf("正しく入力してください\n");
         puts("");
      }

      else if(ret==EOF){
         break;
      }

      ret=scanf("%lf",&dt);
   }
      printf("合計=%f\n",sum2);
      return 0;
}
--------------------------------------------------------

前回、「scanfの入力をgets関数で読み捨てることについて」というタイトルで数値連続加算のプログラムを作り、皆様からいろいろとアドバイスを受けた者です。

いろいろとプログラムを改良し、「Ctrl+Z」の入力でプログラムを終了しようとし、後、前のプログラムでは「10abc」などと打ち込んでも「10」は読み込んでしまうので、「10abc」などと打ち込んだ時点で、エラー表示をさせるようにしました。

ここで疑問なのですが、例えば、
-------
enter
enter
abc
-------
と入力した場合、改行文字が配列ssに格納され、いろいろと複雑になってしまうのかと思ったのですが、ssにはしっかり「abc」だけが格納されていました。

以上のプログラムに不備がないかも含めて、何故そうなるのか教えていただけると嬉しいです。

投稿日時 - 2009-04-06 02:37:04

QNo.4856442

困ってます

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

>ここで疑問なのですが、例えば、

「scanfは、先頭にある空白や改行を読み飛ばしてから、指定の数値を読もうとする」ので
enter
enter
で入れられた改行2つは、scanfが読み飛ばします。

で、scanfが最初の改行を読み飛ばした後で「a」を見付けるので、その「a」は「まだ読んで無い事にして」から、0を返します。

入力バッファには「abc」+「改行」が残っています。

次に「getsは改行まで読み込んで、読み込んだ改行を文字列の終端記号の'\0'に書き替えてから戻って来る」ので「abc\n」を読み込み、\nを\0に書き替えて「abc\0」がssの中に入ります。

ここで問題になるのが「scanfは、先頭にある空白や改行を読み飛ばしてから、指定の数値を読もうとする」と言う部分。

なのでscanfを使う限り「enterだけの入力」で「正しく入力してください」とエラー表示する事は出来ません。

その部分を修正したのが、以下のプログラムです。

#include <stdio.h>

int main(void)
{
 char ss[256];
 double sum=0.0,dt;
 int ret;
 char c;
 char *p;
 for (;;) {
  // scanfは使わないで、とにかく1行全部をssに入力する
  p = gets(ss);

  // Ctrl+Zが入力されたらgetsはNULLを返すので終了する
  if (p == NULL) break;

  // 入力したssから、数値と、数値の直後の文字を取り出す。
  ret = sscanf(ss,"%lf%c",&dt,&c);
  // 改行のみの入力なら「ssが空っぽ」なのでretはEOFになる。
  // 数値だけの入力なら、後ろの%cが入力されないのでretは1になる。
  // 数値以外の入力なら、retは0になる。
  // 数値の後ろに何か余計な物があったらretは2になる。

  // 数値だけ入力したかどうか調べる。
  if (ret == 1) {
   // 数値だけ入力した。
   printf("入力された数値=%f\n",dt);

   // 数値だけ正しく入力したので、sumにdtを足す。
   sum += dt;
  } else {
   // 何も入力せずにEnterだけ押したか、数値以外を入力したか、余計な物を入力した。

   // エラー表示する。
   printf("正しく入力してください\n");
  }
 }
 printf("合計=%f\n",sum);
 return 0;
}

で、これでOKかと言うと、そうじゃない。

この「修正版プログラム」で、以下のように入力してみよう。

1a
1b
1c
1d
1e
1f
Ctrl+Z

結果は

1a
正しく入力してください
1b
正しく入力してください
1c
正しく入力してください
1d
正しく入力してください
1e
入力された数値=1.000000
1f
正しく入力してください
^Z
合計=1.000000

となる。なぜか「1e」だけ「1」として入力され「正しい入力」と判断されてる。

実は、scanfの%lfは「仮数部+e+指数部」と言う入力を許しているのだ。

例えば「120」は「1.2×10の2乗」なので、入力時に「1.2e2」と入力しても良い。

上記の「1e」は「1×10の0乗で、eの後ろの0が省略されてて、1e0と入力した」と認識されてしまう。

「10の0乗」は「1」なので「1×10の0乗」は「1×1」なので、結局は「1」になってしまう。

こういう「意図しない入力を除外したい場合」は「scanfする前に、自前で、入力した文字列が正当か調べる必要」がある。

投稿日時 - 2009-04-06 15:52:45

お礼

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

>改行2つは、scanfが読み飛ばします。
>入力バッファには「abc」+「改行」が残っています。
>「getsは改行まで読み込んで、読み込んだ改行を文字列の終端記号の'\0'
>に書き替えてから戻って来る」ので「abc\n」を読み込み、\nを\0に書き
>替えて「abc\0」がssの中に入ります。

上記の内容、scanfは改行を読み飛ばすという事やgetsの仕様が理解できました!
修正していただいたプログラムも、ステップオーバー実行して理解できました! 簡潔なプログラムになって、とてもわかりやすっかたです。

>scanfの%lfは「仮数部+e+指数部」と言う入力を許しているのだ。
>「意図しない入力を除外したい場合」は「scanfする前に、自前で、入力
>した文字列が正当か調べる必要」がある。

以上のご回答にあるような入力を回避出来るようなプログラム作成、努力してみます。

投稿日時 - 2009-04-07 01:03:29

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

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

回答(2)

ANo.1

>何故そうなるのか教えていただけると嬉しいです。

gets() の仕様です。

投稿日時 - 2009-04-06 03:28:42