91

I have the following 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;
}

As the author of the above code have explained: The program will not work properly because at Line 1, when the user presses Enter, it will leave in the input buffer 2 character: Enter key (ASCII code 13) and \n (ASCII code 10). Therefore, at Line 2, it will read the \n and will not wait for the user to enter a character.

OK, I got this. But my first question is: Why the second getchar() (ch2 = getchar();) does not read the Enter key (13), rather than \n character?

Next, the author proposed 2 ways to solve such probrems:

  1. use fflush()

  2. write a function like this:

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

This code worked actually. But I cannot explain myself how it works? Because in the while statement, we use getchar() != '\n', that means read any single character except '\n'? if so, in the input buffer still remains the '\n' character?

Steven Penny
  • 82,115
  • 47
  • 308
  • 348
ipkiss
  • 12,171
  • 26
  • 81
  • 118

12 Answers12

97

The program will not work properly because at Line 1, when the user presses Enter, it will leave in the input buffer 2 character: Enter key (ASCII code 13) and \n (ASCII code 10). Therefore, at Line 2, it will read the \n and will not wait for the user to enter a character.

The behavior you see at line 2 is correct, but that's not quite the correct explanation. With text-mode streams, it doesn't matter what line-endings your platform uses (whether carriage return (0x0D) + linefeed (0x0A), a bare CR, or a bare LF). The C runtime library will take care of that for you: your program will see just '\n' for newlines.

If you typed a character and pressed enter, then that input character would be read by line 1, and then '\n' would be read by line 2. See I'm using scanf %c to read a Y/N response, but later input gets skipped. from the comp.lang.c FAQ.

As for the proposed solutions, see (again from the comp.lang.c FAQ):

which basically state that the only portable approach is to do:

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

Your getchar() != '\n' loop works because once you call getchar(), the returned character already has been removed from the input stream.

Also, I feel obligated to discourage you from using scanf entirely: Why does everyone say not to use scanf? What should I use instead?

jamesdlin
  • 48,496
  • 10
  • 105
  • 134
  • 1
    This will hang waiting for the user to press Enter. If you want to simply clear stdin, use termios directly. https://stackoverflow.com/a/23885008/544099 – ishmael Feb 25 '19 at 14:39
  • 1
    @ishmael Your comment doesn't make sense since pressing Enter isn't required to *clear* stdin; it's required to get input in the first place. (And that's the only *standard* way to read input in C. If you want to be able to read keys immediately, then you'll have to use non-standard libraries.) – jamesdlin Feb 25 '19 at 15:54
  • @jamesdlin On Linux, getchar() will wait for the user to press Enter. Only then will they break out of the while loop. As far as I'm aware, termios is a standard library. – ishmael Feb 25 '19 at 21:04
  • @ishmael No, you're wrong on both counts. This question is about how about clearing the remaining input from stdin *after* reading input, and in standard C, providing that input *first* requires typing a line and pressing Enter. *After* that line has been entered, `getchar()` will read and return those characters; a user will not need to press Enter another time. Furthermore, while termios might be common *on Linux*, it is not part of the *C standard library*. – jamesdlin Feb 25 '19 at 21:17
  • what is the reason for `scanf(" %c", &char_var)` working and ignoring the newline just by adding a space befor %c specifier? – Cătălina Sîrbu Sep 29 '20 at 17:52
  • @CătălinaSîrbu `scanf` collapses all whitespace and treats all whitespace as equivalent. For example, a space in the format string will match any number of newlines, spaces, and tabs, and also can match nothing. – jamesdlin Sep 29 '20 at 20:04
  • Using `scanf` itself is never a good practice. `getline`/`fgets` with `sscanf` is better. Whether or not the whitespace collapsing behavior of the `scanf`-family of functions is desirable depends on your situation. – jamesdlin Sep 29 '20 at 22:10
47

You can do it (also) this way:

fseek(stdin,0,SEEK_END);
Ramy Al Zuhouri
  • 20,942
  • 23
  • 94
  • 176
  • Wow... btw, does standard say `fseek` is available for `stdin`? – ikh May 22 '14 at 11:03
  • 3
    I don't know, I think it doesn't mention it, but stdin is FILE* and fseek accepts a FILE* parameter. I tested it and it works on Mac OS X but not on Linux. – Ramy Al Zuhouri May 22 '14 at 11:07
  • Please explain. It will be a great answer if the author would write implications of this method. Are there any issues? – EvAlex Oct 28 '15 at 07:35
  • 7
    @EvAlex: there are many issues with this. If it works on your system, great; if not, then it is not surprising as nothing guarantees that it will work when standard input is an interactive device (or a non-seekable device like a pipe or a socket or a FIFO, to name but a few other ways in which it can fail). – Jonathan Leffler Sep 15 '16 at 05:08
  • Why it should be SEEK_END? Can we use SEEK_SET instead? How is the FP stdin behave? – Rajesh Mar 03 '17 at 11:33
  • Works like a charm on Windows! Much cleaner and better way of flushing the keyboard buffer than using a loop as suggested by others. Thanks a ton! – Anurag S Sharma Sep 29 '18 at 10:48
  • @ikh: No, it is not. `SEEK_END` cannot be used with text streams. So, this as bad as `fflush(stdin)`. – AnT Jun 15 '19 at 00:29
  • it does not work. When choosing grid from 1 to 9 elements I can enter '95' and it will choose 9 then 5. – mLstudent33 Jul 02 '20 at 19:58
12

A portable way to clear up to the end of a line that you've already tried to read partially is:

int c;

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

This reads and discards characters until it gets \n which signals the end of the file. It also checks against EOF in case the input stream gets closed before the end of the line. The type of c must be int (or larger) in order to be able to hold the value EOF.

There is no portable way to find out if there are any more lines after the current line (if there aren't, then getchar will block for input).

M.M
  • 130,300
  • 18
  • 171
  • 314
  • why while((c=getchar())!=EOF); does not work? It is endless loop. Not able to understand the location of EOF in stdin. – Rajesh Mar 03 '17 at 15:57
  • @Rajesh That would clear until the input stream is closed, which it won't be if there is more input to come later – M.M Mar 03 '17 at 21:45
  • @Rajesh Yes, you are correct. If there is nothing on stdin to flush when this gets called, it will block(hang) due to it being caught in an infinite loop. – Jason Enochs Jul 13 '17 at 21:31
11

The lines:

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

doesn't read only the characters before the linefeed ('\n'). It reads all the characters in the stream (and discards them) up to and including the next linefeed (or EOF is encountered). For the test to be true, it has to read the linefeed first; so when the loop stops, the linefeed was the last character read, but it has been read.

As for why it reads a linefeed instead of a carriage return, that's because the system has translated the return to a linefeed. When enter is pressed, that signals the end of the line... but the stream contains a line feed instead since that's the normal end-of-line marker for the system. That might be platform dependent.

Also, using fflush() on an input stream doesn't work on all platforms; for example it doesn't generally work on Linux.

Jonathan Leffler
  • 666,971
  • 126
  • 813
  • 1,185
Dmitri
  • 8,455
  • 2
  • 24
  • 31
8

But I cannot explain myself how it works? Because in the while statement, we use getchar() != '\n', that means read any single character except '\n'?? if so, in the input buffer still remains the '\n' character??? Am I misunderstanding something??

The thing you may not realize is that the comparison happens after getchar() removes the character from the input buffer. So when you reach the '\n', it is consumed and then you break out of the loop.

zwol
  • 121,956
  • 33
  • 219
  • 328
6

you can try

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

where %*c accepts and ignores the newline

one more method instead of fflush(stdin) which invokes undefined behaviour you can write

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

don't forget the semicolon after while loop

kapil
  • 577
  • 11
  • 20
  • 2
    1) `"%*c"` scans any character ( and does not save it), be it a newline or something else. Code is relying on that the 2nd character is a new line. 2) `while((getchar())!='\n');` is an infinite loop should `getchar()` return `EOF` due to end-of-file. – chux - Reinstate Monica Sep 10 '15 at 15:12
  • 1
    the second method depends on the condition like newline,null character,EOF etc(above it was newline) – kapil Sep 11 '15 at 12:33
1

I encounter a problem trying to implement the solution

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

I post a little adjustment 'Code B' for anyone who maybe have the same problem.

The problem was that the program kept me catching the '\n' character, independently from the enter character, here is the code that gave me the problem.

Code A

int y;

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

and the adjustment was to 'catch' the (n-1) character just before the conditional in the while loop be evaluated, here is the 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));

The possible explanation is that for the while loop to break, it has to assign the value '\n' to the variable y, so it will be the last assigned value.

If I missed something with the explanation, code A or code B please tell me, I’m barely new in c.

hope it helps someone

antadlp
  • 39
  • 2
  • You should deal with your "*expected*" character(s) inside of the `while` loop. After the loop you can consider `stdin` to be *clean/clear* and `y` will hold either '`\n`' or `EOF`. `EOF` is returned when there is no newline present and the buffer is exhausted (if the input would overflow the buffer before `[ENTER]` was pressed). *-- You effectively use the `while((ch=getchar())!='\n'&&ch!=EOF);` to chew up each and every character in the `stdin` input buffer.* – veganaiZe Jul 29 '17 at 01:31
1

Try this:

stdin->_IO_read_ptr = stdin->_IO_read_end;

  • 4
    Please add a bit more details about how this solution works and how it is a useful way to solve the problem https://stackoverflow.com/help/how-to-answer – Suit Boy Apps Jul 25 '20 at 21:05
0
unsigned char a=0;
if(kbhit()){
    a=getch();
    while(kbhit())
        getch();
}
cout<<hex<<(static_cast<unsigned int:->(a) & 0xFF)<<endl;

-or-

use maybe use _getch_nolock() ..???

Tony
  • 5,213
  • 1
  • 31
  • 47
0

Another solution not mentioned yet is to use: rewind(stdin);

James Blue
  • 90
  • 6
  • 2
    That will completely break the program if stdin is redirected to a file. And for interactive input it's not guaranteed to do anything. – melpomene Jul 29 '19 at 04:16
0

I am surprised nobody mentioned this:

scanf("%*[^\n]");
nowox
  • 19,233
  • 18
  • 91
  • 202
-1

Short, portable and declared in stdio.h

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

Doesn't get hung in an infinite loop when there is nothing on stdin to flush like the following well know line:

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

A little expensive so don't use it in a program that needs to repeatedly clear the buffer.

Stole from a coworker :)

Jason Enochs
  • 1,350
  • 1
  • 11
  • 19