Invoerbuffer in C wissen in C?

Ik heb het volgende programma:

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

Zoals de auteur van de bovenstaande code heeft uitgelegd:
Het programma zal niet goed werken, omdat op regel 1, wanneer de gebruiker op ENTER drukt, het in het ingangsbuffer 2-tekens zal vertrekken: Enter key (ASCII code 13)en \n (ASCII code 10). Daarom zal op regel 2 de \nlezen en wacht niet op de gebruiker om een ​​teken in te voeren.

OK, ik heb dit. Maar mijn eerste vraag is: waarom de tweede getchar()(ch2 = getchar();) niet leest de Enter key (13), in plaats van \nteken?

Vervolgens stelde de auteur 2 manieren voor om dergelijke probrems op te lossen:

  1. Gebruik fflush()

  2. Schrijf een functie als volgt:

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

Deze code werkte eigenlijk. Maar ik kan mezelf niet uitleggen hoe het werkt? Want in de while-verklaring, gebruiken we getchar() != '\n', dat betekent het lezen van een enkel teken, behalve '\n'? Zo ja, blijft in de invoerbuffer nog steeds de '\n'teken?


Antwoord 1, Autoriteit 100%

Het programma zal niet goed werken omdat op regel 1, wanneer de gebruiker op Enter drukt, het teken in de invoerbuffer 2 achterblijft: Enter-toets (ASCII-code 13) en \n (ASCII-code 10). Daarom leest het op regel 2 de \n en wacht het niet tot de gebruiker een teken invoert.

Het gedrag dat u op regel 2 ziet, is correct, maar dat is niet helemaal de juiste verklaring. Met streams in tekstmodus maakt het niet uit welke regeleinden uw platform gebruikt (of het nu gaat om een regelterugloop (0x0D) + regelinvoer (0x0A), een kale CR of een kale LF). De C runtime-bibliotheek regelt dat voor u: uw programma ziet alleen '\n'voor nieuwe regels.

Als je een teken hebt getypt en op enter drukt, wordt dat invoerteken gelezen door regel 1 en dan wordt '\n'gelezen door regel 2. Zie Ik gebruik scanf %com een J/N-antwoord te lezen, maar latere invoer wordt overgeslagen.van de comp.lang.c FAQ.

Voor de voorgestelde oplossingen, zie (opnieuw van de comp.lang.c FAQ):

waarin in feite staat dat de enige draagbare benadering is om te doen:

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

Uw getchar() != '\n'lus werkt omdat zodra u getchar()aanroept, het geretourneerde teken al uit de invoerstroom is verwijderd.

Ik voel me ook verplicht om u te ontmoedigen scanfvolledig te gebruiken: Waarom zegt iedereen scanfniet te gebruiken? Wat moet ik in plaats daarvan gebruiken?


Antwoord 2, autoriteit 46%

Je kunt het (ook) op deze manier doen:

fseek(stdin,0,SEEK_END);

Antwoord 3, autoriteit 13%

Een draagbare manier om tot het einde van een regel te wissen die u al gedeeltelijk heeft proberen te lezen, is:

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

Hiermee worden tekens gelezen en verwijderd totdat \nhet einde van het bestand aangeeft. Het controleert ook tegen EOFin het geval dat de invoerstroom wordt afgesloten voor het einde van de regel. Het type cmoet int(of groter) zijn om de waarde EOFte kunnen bevatten.

Er is geen draagbare manier om erachter te komen of er nog meer regels zijn na de huidige regel (zo niet, dan blokkeert getcharvoor invoer).


Antwoord 4, autoriteit 11%

De regels:

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

lees niet alleen de tekens vóór de regelinvoer ('\n'). Het leest alle tekens in de stream (en verwijdert ze) tot en metde volgende regelinvoer (of EOF wordt aangetroffen). Om de test waar te maken, moet deze eerst de linefeed lezen; dus wanneer de lus stopt, was de regelinvoer het laatst gelezen teken, maar het is gelezen.

De reden waarom het een regelinvoer leest in plaats van een regelterugloop, dat komt omdat het systeem de terugkeer naar een regelterugloop heeft vertaald. Wanneer enter wordt ingedrukt, geeft dat het einde van de regel aan… maar de stream bevat in plaats daarvan een regelinvoer, aangezien dat de normale einde-lijnmarkering voor het systeem is. Dat kan platformafhankelijk zijn.

Bovendien werkt het gebruik van fflush()op een invoerstroom niet op alle platforms; het werkt bijvoorbeeld over het algemeen niet op Linux.


Antwoord 5, autoriteit 8%

Maar ik kan zelf niet uitleggen hoe het werkt? Omdat we in het while-statement getchar() != '\n'gebruiken, betekent dit dat je elk afzonderlijk teken moet lezen behalve '\n'?? zo ja, dan blijft in de invoerbuffer nog steeds het teken '\n'???
Begrijp ik iets verkeerd??

Wat u zich misschien niet realiseert, is dat de vergelijking nadatgetchar()het teken uit de invoerbuffer wordt verwijderd. Dus wanneer je de '\n'bereikt, wordt deze verbruikt en vervolgensbreek je uit de lus.


Antwoord 6, autoriteit 6%

je kunt het proberen

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

waar %*c de nieuwe regel accepteert en negeert

nog een methode
in plaats van fflush(stdin) dat ongedefinieerd gedrag oproept, kun je schrijven

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

vergeet de puntkomma niet na de while-lus


Antwoord 7

Ik ondervind een probleem bij het implementeren van de oplossing

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

Ik plaats een kleine aanpassing ‘Code B’ voor iedereen die hetzelfde probleem heeft.

Het probleem was dat het programma ervoor zorgde dat ik het ‘\n’-teken bleef vangen, onafhankelijk van het enter-teken, hier is de code die me het probleem gaf.

Code A

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

en de aanpassing was om het (n-1) teken te ‘vangen’ net voordat de conditionele in de while-lus wordt geëvalueerd, hier is de code:

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

De mogelijke verklaring is dat om de while-lus te breken, deze de waarde ‘\n’ moet toewijzen aan de variabele y, dus het zal de laatst toegewezen waarde zijn.

Als ik iets heb gemist met de uitleg, code A of code B, zeg het me dan, ik ben amper nieuw in c.

ik hoop dat het iemand helpt


Antwoord 8

Probeer dit:

stdin->_IO_read_ptr = stdin->_IO_read_end;


Antwoord 9

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

-of-

gebruik misschien _getch_nolock()..???


Antwoord 10

Een andere nog niet genoemde oplossing is het gebruik van:
terugspoelen(stdin);


Antwoord 11

Het verbaast me dat niemand dit heeft genoemd:

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

Antwoord 12

In het kort. De streep zetten…

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

…voor de regel is het lezen van de invoer de enige gegarandeerde methode.
Het gebruikt alleen kern C-functies (“conventionele kern van C-taal” volgens K&R) die gegarandeerd werken met alle compilers onder alle omstandigheden.

In werkelijkheid kun je ervoor kiezen om een prompt toe te voegen waarin je een gebruiker vraagt om op ENTER te drukken om door te gaan (of, optioneel, op Ctrl-D of een andere knop om te voltooien of om een andere code uit te voeren):

printf("\nPress ENTER to continue, press CTRL-D when finished\n");    
while ((c = getchar()) != '\n') {
        if (c == EOF) {
            printf("\nEnd of program, exiting now...\n");
            return 0;
        }
        ...
    }

Er is nog steeds een probleem. Een gebruiker kan vaak op ENTER drukken. Dit kan worden omzeild door een invoercontrole toe te voegen aan uw invoerregel:

while ((ch2 = getchar()) < 'a' || ch1 > 'Z') ;

Een combinatie van de bovenstaande twee methoden zou in theorie kogelvrij moeten zijn.
In alle andere aspecten is het antwoord van @jamesdlin het meest uitgebreid.


Antwoord 13

Kort, draagbaar en gedeclareerd in stdio.h

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

Blijft niet in een oneindige lus hangen als er niets op stdin staat om door te spoelen, zoals de volgende bekende regel:

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

Een beetje duur, dus gebruik het niet in een programma dat herhaaldelijk de buffer moet wissen.

Gestolen van een collega 🙂

Other episodes