Jak wyczyścić bufor wejściowy w C?

Mam następujący program:

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;
}

Jak wyjaśnił autor powyższego kodu: Program nie będzie działał poprawnie, ponieważ w linii 1, gdy użytkownik naciśnie Enter, zostanie pozostawiony w buforze wejściowym znak 2: Enter key (ASCII code 13) i \n (ASCII code 10). Dlatego w linii 2 odczyta \n i nie będzie czekać, aż użytkownik wprowadzi znak.

Ok, zajmę się tym. Ale moje pierwsze pytanie brzmi: dlaczego drugi getchar() (ch2 = getchar();) nie czyta Enter key (13), zamiast \n charakter?

Następnie autor zaproponował 2 sposoby rozwiązania takich probremów:

  1. Użycie fflush()

  2. Napisz taką funkcję:

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

Ten kod zadziałał. Ale nie mogę się wytłumaczyć, jak to działa? Ponieważ w instrukcji while używamy getchar() != '\n', czyli odczytujemy dowolny pojedynczy znak oprócz '\n'? jeśli tak, to w buforze wejściowym nadal pozostaje znak '\n'?

 61
Author: Steven Penny, 2011-10-26

11 answers

Program nie będzie działał poprawnie, ponieważ w linii 1, gdy użytkownik naciśnie Enter, zostanie pozostawiony w buforze wejściowym znak 2: klawisz Enter (kod ASCII 13) i \n (kod ASCII 10). Dlatego w linii 2 odczyta \n i nie będzie czekać, aż użytkownik wprowadzi znak.

Zachowanie, które widzisz na linii 2 jest poprawne, ale to nie jest do końca poprawne Wyjaśnienie. W przypadku strumieni w trybie tekstowym nie ma znaczenia, jakich linii używa twoja platforma (czy powrót karetki (0x0D) + linefeed (0x0A), bare CR lub bare LF). Biblioteka uruchomieniowa C zajmie się tym za ciebie: twój program zobaczy tylko '\n' dla nowych linii.

Jeśli wpiszesz znak i wciśniesz enter, to ten znak wejściowy zostanie odczytany w linii 1, a następnie '\n' zostanie odczytany w linii 2. Zobacz używam scanf %c, aby odczytać odpowiedź Y/ N, ale później Dane wejściowe są pomijane. z komp.lang.c FAQ.

Jeśli chodzi o proponowane rozwiązania, zobacz (ponownie z komp.lang.c FAQ):

Które zasadniczo stwierdzają, że jedynym przenośnym podejściem jest zrobić:

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

Twoja pętla getchar() != '\n' działa, ponieważ po wywołaniu getchar(), zwrócony znak został już usunięty ze strumienia wejściowego.

Również, czuję się zobowiązany do zniechęcić cię do używania scanf całkowicie: dlaczego wszyscy mówią, aby nie używać scanf? Czego powinienem użyć zamiast tego?

 62
Author: jamesdlin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-18 00:52:04

Możesz to zrobić (również) w ten sposób:

fseek(stdin,0,SEEK_END);
 37
Author: Ramy Al Zuhouri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-17 13:36:48

Linie:

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

Nie odczytuje tylko znaków przed linią ('\n'). Odczytuje wszystkie znaki w strumieniu (i odrzuca je) aż do następnego wiersza włącznie (lub napotkany jest EOF). Aby test był prawdziwy, musi najpierw odczytać strumień liniowy; więc kiedy pętla się zatrzyma, strumień liniowy był ostatnim odczytanym znakiem, ale został odczytany.

A co do tego, dlaczego czyta linefeed zamiast return carriage, to dlatego, że system przetłumaczył powrót do liniowca. Po naciśnięciu enter sygnalizuje koniec linii... ale strumień zawiera kanał liniowy, ponieważ jest to normalny znacznik końca linii dla systemu. To może być zależne od platformy.

Również użycie fflush() na strumieniu wejściowym nie działa na wszystkich platformach; na przykład ogólnie nie działa na Linuksie.

 11
Author: Dmitri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-15 05:10:37

Przenośnym sposobem na wyczyszczenie końca wiersza, który już próbowałeś przeczytać częściowo jest:

int c;

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

To odczytuje i odrzuca znaki, dopóki nie otrzyma \n, Co zasygnalizuje koniec pliku. Sprawdza również przed EOF w przypadku, gdy strumień wejściowy zostanie zamknięty przed końcem linii. Typ c musi być int (lub większy), aby móc utrzymać wartość EOF.

Nie ma przenośnego sposobu, aby dowiedzieć się, czy jest więcej linii po bieżącej linii (jeśli nie ma, wtedy getchar zablokuje wejście).

 10
Author: M.M,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-28 02:47:18
Ale nie potrafię wyjaśnić, jak to działa? Ponieważ w instrukcji while używamy getchar() != '\n', co oznacza odczyt dowolnego pojedynczego znaku poza '\n'?? jeśli tak, to w buforze wejściowym nadal pozostaje znak '\n'??? Czy coś źle zrozumiałam??

Rzecz może nie zdawać sobie sprawy, że porównanie dzieje się po getchar() usuwa znak z bufora wejściowego. Więc kiedy osiągniesz '\n', jest zużyty i wtedy wyłamujesz się z pętla.

 7
Author: zwol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-26 03:12:43

Możesz spróbować

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

Gdzie % * c akceptuje i ignoruje nową linię

Jeszcze jedna metoda zamiast fflush (stdin) wywołującego nieokreślone zachowanie można napisać

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

Nie zapomnij średnika po pętli while

 6
Author: kapil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-10 15:04:30
unsigned char a=0;
if(kbhit()){
    a=getch();
    while(kbhit())
        getch();
}
cout<<hex<<(static_cast<unsigned int:->(a) & 0xFF)<<endl;

-lub -

Użyj może użyj _getch_nolock() ..???

 1
Author: Michael Grieswald,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-21 03:17:55

Napotkałem problem próbując zaimplementować rozwiązanie

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

Zamieszczam mały 'kod B' dla każdego, kto może mieć ten sam problem.

Problem polegał na tym, że program pozwalał mi łapać znak '\n', niezależnie od znaku enter, oto kod, który dał mi problem.

Kod A

int y;

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

I korekta polegała na "złapaniu" znaku (n-1) tuż przed obliczeniem warunkowego w pętli while, oto kod:

Kod 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));

Możliwe wyjaśnienie jest takie, że dla przerwania pętli while, musi ona przypisać wartość' \n ' do zmiennej y, więc będzie to ostatnia przypisana wartość.

Jeśli coś przeoczyłem z wyjaśnieniem, kodem a lub kodem B, proszę mi powiedzieć, Jestem ledwo nowy w c.

Hope it helps someone

 1
Author: antadlp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-20 15:26:04

Krótki, przenośny i zadeklarowany w stdio.h

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

Nie jest zawieszony w nieskończonej pętli, gdy na stdin nie ma nic do spłukania, jak następująca dobrze znana linia:

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

Trochę drogi, więc nie używaj go w programie, który musi wielokrotnie czyścić bufor.

Ukradł współpracownikowi:)

 0
Author: Jason Enochs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-22 21:28:24
char choice;
do{
    printf("\n *");
    printf("\n Do you want to continue? (y/n) ");
    choice = getchar();
    if(getchar() != '\n'){
        fflush(stdin);
    }
}while( choice != 'n' );
 0
Author: Vishal T eli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-12 17:56:16

Innym nie wspomnianym jeszcze rozwiązaniem jest użycie: rewind (stdin);

 0
Author: James Blue,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-26 02:40:41