Cで入力バッファをクリアする方法は?


84

私は次のプログラムを持っています:

int main(int argc, char *argv[])
{
  char ch1, ch2;
  printf("Input the first character:"); // Line 1
  scanf("%c", &ch1); 
  printf("Input the second character:"); // Line 2
  ch2 = getchar();

  printf("ch1=%c, ASCII code = %d\n", ch1, ch1);
  printf("ch2=%c, ASCII code = %d\n", ch2, ch2);

  system("PAUSE");  
  return 0;
}

上記のコードの作者のように説明している:ユーザーがEnterキーを押すライン1、で、それは2文字バッファ入力に残すため、プログラムが正しく動作しません:Enter key (ASCII code 13)\n (ASCII code 10)。したがって、2行目では、を読み取り\n、ユーザーが文字を入力するのを待ちません。

OK、これだ。しかし、私の最初の質問は、2番目のgetchar()ch2 = getchar();)が文字Enter key (13)ではなく、を読み取らないのはなぜ\nですか?

次に、著者はそのような問題を解決するための2つの方法を提案しました。

  1. 使用する fflush()

  2. 次のような関数を記述します。

    void
    clear (void)
    {    
      while ( getchar() != '\n' );
    }
    

このコードは実際に機能しました。しかし、私はそれがどのように機能するかを自分自身で説明することはできませんか?whileステートメントでは、を使用しているため、?getchar() != '\n'以外の任意の1文字を読み取ることを意味します'\n'。もしそうなら、入力バッファにはまだ'\n'文字が残っていますか?

回答:


89

1行目でユーザーがEnterキーを押すと、Enterキー(ASCIIコード13)と\ n(ASCIIコード10)の2文字が入力バッファーに残るため、プログラムは正しく機能しません。したがって、2行目では、\ nを読み取り、ユーザーが文字を入力するのを待ちません。

2行目に表示される動作は正しいですが、それは完全に正しい説明ではありません。テキストモードストリームでは、プラットフォームが使用する行末(キャリッジリターン(0x0D)+ラインフィード(0x0A)、ベアCR、ベアLF)は関係ありません。Cランタイムライブラリがそれを処理します。プログラムは'\n'改行だけを認識します。

文字を入力してEnterキーを押すと、その入力文字は1行目で読み取られ、次に'\n'2行目で読み取られます。Y/ N応答の読み取りに使用していますが、後で入力がスキップされる参照してくださいscanf %ccomp.lang.cFAQから。

提案されたソリューションについては、(ここでもcomp.lang.c FAQから)を参照してください。

これは基本的に、唯一の移植可能なアプローチは次のことを行うことであると述べています。

int c;
while ((c = getchar()) != '\n' && c != EOF) { }

あなたのgetchar() != '\n'ループは、お電話の後にあるため動作しますgetchar()、返された文字がすでに入力ストリームから削除されました。

また、私はあなたがscanf完全に使用することを思いとどまらせる義務があると感じています:なぜ誰もが使用しないと言うのscanfですか?代わりに何を使用すればよいですか?


1
これは、ユーザーがEnterキーを押すのを待ってハングします。stdinを単純にクリアしたい場合は、termiosを直接使用してください。 stackoverflow.com/a/23885008/544099
イシュマエル

@ishmael stdinをクリアするためにEnterキーを押す必要がないため、コメントは意味がありません。そもそも入力を得る必要があります。(これがCで入力を読み取る唯一の標準的な方法です。キーをすぐに読み取れるようにする場合は、非標準のライブラリを使用する必要があります。)
jamesdlin19年

@jamesdlin Linuxでは、getchar()はユーザーがEnterキーを押すのを待ちます。そうして初めて、whileループから抜け出します。私の知る限り、termiosは標準ライブラリです。
イシュマエル

@ishmaelいいえ、あなたは両方の点で間違っています。この質問は、入力を読み取った、stdinから残りの入力をクリアする方法についてです。標準Cでは、最初に入力を入力してEnterキーを押す必要があります。 その行が入力されたgetchar()はそれらの文字を読み取って返します。ユーザーはEnterキーをもう一度押す必要はありません。さらに、termiosはLinuxで一般的かもしれませんが、C標準ライブラリの一部ではありません。
jamesdlin

scanf(" %c", &char_var)%c指定子の前にスペースを追加するだけで改行を処理して無視する理由は何ですか?
カタリナSIRBU

44

あなたはそれを(また)このように行うことができます:

fseek(stdin,0,SEEK_END);

うわー...ところで、標準的な言い方fseekは利用できますstdinか?
ikh 2014年

3
わかりません。言及されていないと思いますが、stdinはFILE *であり、fseekはFILE *パラメーターを受け入れます。私はそれをテストしました、そしてそれはMac OS Xで動作しますが、Linuxでは動作しません。
Ramy Al Zuhouri 2014年

説明してください。著者がこの方法の意味を書くならば、それは素晴らしい答えになるでしょう。何か問題はありますか?
EvAlex 2015年

7
@EvAlex:これには多くの問題があります。それがあなたのシステムで動作するなら、素晴らしいです。そうでない場合は、標準入力がインタラクティブデバイス(またはパイプ、ソケット、FIFOなどのシーク不可能なデバイス)の場合に機能することを保証するものは何もないので、驚くことではありませんが、他のいくつかの方法で可能です。不合格)。
ジョナサンレフラー2016

なぜSEEK_ENDにする必要があるのですか?代わりにSEEK_SETを使用できますか?FP stdinはどのように動作しますか?
ラジェッシュ2017

11

すでに部分的に読み込もうとした行の終わりまでクリアするためのポータブルな方法は次のとおりです。

int c;

while ( (c = getchar()) != '\n' && c != EOF ) { }

これ\nは、ファイルの終わりを示す信号を取得するまで、文字を読み取って破棄します。またEOF、行の終わりの前に入力ストリームが閉じられたかどうかをチェックします。値を保持できるようにするには、のタイプcint(またはそれ以上)である必要がありますEOF

現在の行の後に行があるかどうかを確認する移植可能な方法はありません(行がない場合は、getchar入力をブロックします)。


なぜwhile((c = getchar())!= EOF); 動作しません?無限ループです。stdinのEOFの場所を理解できません。
ラジェッシュ2017

@Rajeshこれは、入力ストリームが閉じられるまでクリアされますが、後で来る入力がさらにある場合はクリアされません
MM

@Rajeshはい、その通りです。これが呼び出されたときにフラッシュするstdinに何もない場合、無限ループに巻き込まれているため、ブロック(ハング)します。
Jason Enochs 2017

11

台詞:

int ch;
while ((ch = getchar()) != '\n' && ch != EOF)
    ;

改行('\n')の前の文字だけを読み取りません。次の改行まで(またはEOFが検出されるまで)、ストリーム内のすべての文字を読み取ります(そしてそれらを破棄します)。テストが真であるためには、最初にラインフィードを読み取る必要があります。したがって、ループが停止すると、改行は最後に読み取られた文字でしたが、読み取られました。

キャリッジリターンではなくラインフィードを読み取る理由については、システムがリターンをラインフィードに変換したためです。Enterキーを押すと、それは行の終わりを示します...しかし、それはシステムの通常の行末マーカーであるため、ストリームには代わりに改行が含まれます。それはプラットフォームに依存するかもしれません。

また、fflush()入力ストリームでの使用は、すべてのプラットフォームで機能するとは限りません。たとえば、Linuxでは通常は機能しません。


注: ファイルの終わりが原因でwhile ( getchar() != '\n' );無限ループがgetchar()返さEOFれるはずです。
chux -復活モニカ

EOFもチェックするには、int ch; while ((ch = getchar()) != '\n' && ch != EOF);を使用できます
Dmitri

8

しかし、私はそれがどのように機能するかを自分自身で説明することはできませんか?whileステートメントでは、を使用しているため、??getchar() != '\n'以外の任意の1文字を読み取ることを意味します。'\n'もしそうなら、入力バッファにはまだ'\n'文字が残っていますか?私は何かを誤解していますか?

気付かないかもしれませんが、入力バッファから文字を削除した後に 比較が行われるということですgetchar()。あなたが到達したときので'\n'、それが消費され、その後、あなたはループから抜け出します。


6

あなたが試すことができます

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

ここで、%* cは改行を受け入れて無視します

未定義の動作を呼び出すfflush(stdin)の代わりにもう1つのメソッドを記述できます

while((getchar())!='\n');

whileループの後のセミコロンを忘れないでください


2
1)"%*c"改行であろうとなかろうと、任意の文字をスキャンします(保存しません)。コードは、2番目の文字が改行であることを信頼しています。2)ファイルの終わりのためにwhile((getchar())!='\n');無限ループがgetchar()返さEOFれるはずです。
chux -復活モニカ

1
2番目の方法は、改行、ヌル文字、EOFなどの条件によって異なります(上記は改行でした)
kapil 2015

1

ソリューションを実装しようとして問題が発生しました

while ((c = getchar()) != '\n' && c != EOF) { }

同じ問題を抱えている人のために、少し調整した「コードB」を投稿します。

問題は、プログラムが、入力文字とは関係なく、「\ n」文字をキャッチし続けたことでした。これが、問題を引き起こしたコードです。

コードA

int y;

printf("\nGive any alphabetic character in lowercase: ");
while( (y = getchar()) != '\n' && y != EOF){
   continue;
}
printf("\n%c\n", toupper(y));

調整は、whileループの条件が評価される直前に(n-1)文字を「キャッチ」することでした。コードは次のとおりです。

コードB

int y, x;

printf("\nGive any alphabetic character in lowercase: ");
while( (y = getchar()) != '\n' && y != EOF){
   x = y;
}
printf("\n%c\n", toupper(x));

考えられる理由は、whileループを中断するには、変数yに値 '\ n'を割り当てる必要があるため、最後に割り当てられた値になるというものです。

説明、コードAまたはコードBで何かを見逃した場合は、教えてください、私はcでほとんど新しいです。

それが誰かを助けることを願っています


ループ内で「期待される」文字を処理する必要がありwhileます。ループの後、あなたは次のように考えることができstdinますクリーン/クリアy「のいずれかを保持します\n」かEOFEOF改行が存在せず、バッファが使い果たされた場合に返されます(入力[ENTER]が押される前にバッファをオーバーフローした場合)。 -を効果的に使用しwhile((ch=getchar())!='\n'&&ch!=EOF);て、stdin入力バッファ内のすべての文字をかみ砕きます。
veganaiZe 2017

0
unsigned char a=0;
if(kbhit()){
    a=getch();
    while(kbhit())
        getch();
}
cout<<hex<<(static_cast<unsigned int:->(a) & 0xFF)<<endl;

-または-

使用多分使用_getch_nolock().. ???


問題はCについてであり、C ++についてではありません。
spikatrix 2015年

1
kbhit()およびgetch()はで宣言された関数で<conio.h>あり、Windowsでは標準としてのみ使用可能であることに注意してください。これらはUnixライクなマシンでエミュレートできますが、そうするには注意が必要です。
ジョナサンレフラー2016

0

まだ言及されていない別の解決策は、使用することです:rewind(stdin);


2
stdinがファイルにリダイレクトされると、プログラムが完全に壊れます。また、インタラクティブな入力の場合、何もすることが保証されていません。
メルポメネー

0

私は誰もこれに言及していないことに驚いています:

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

-1

短く、ポータブルで、stdio.hで宣言されています

stdin = freopen(NULL,"r",stdin);

次のよく知られている行のように、フラッシュするstdinに何もない場合でも、無限ループに陥ることはありません。

while ((c = getchar()) != '\n' && c != EOF) { }

少し高価なので、繰り返しバッファをクリアする必要のあるプログラムでは使用しないでください。

同僚から盗んだ:)


Linuxでは動作しません。代わりにtermiosを直接使用してください。 stackoverflow.com/a/23885008/544099
イシュマエル

2
よく知られている線が無限ループの可能性がある理由を詳しく説明してください。
カカフエテフリトレー

全体的な理由freopenは、に移植可能に割り当てることができないということですstdin。それはマクロです。
メルポメネー

1
ishmael:ここCyber​​ CommandにあるコンピューターはすべてLinuxであり、正常に動作します。DebianとFedoraの両方でテストしました。
JasonEnochs19年

「無限ループ」と呼ばれるのは、入力を待機しているプログラムです。それは同じことではありません。
jamesdlin

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.