94
votes

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?

13

13 Answers

102
votes

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?

47
votes

You can do it (also) this way:

fseek(stdin,0,SEEK_END);
13
votes

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).

11
votes

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.

8
votes

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.

6
votes

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

1
votes

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

1
votes

Try this:

stdin->_IO_read_ptr = stdin->_IO_read_end;

0
votes
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() ..???

0
votes

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

0
votes

I am surprised nobody mentioned this:

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

In brief. Putting the line...

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

...before the line reading the input is the only guaranteed method. It uses only core C features ("conventional core of C language" as per K&R) which are guaranteed to work with all compilers in all circumstances.

In reality you may choose to add a prompt asking a user to hit ENTER to continue (or, optionally, hit Ctrl-D or any other button to finish or to perform other code):

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

There is still a problem. A user can hit ENTER many times. This can be worked around by adding an input check to your input line:

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

Combination of the above two methods theoretically should be bulletproof. In all other aspects the answer by @jamesdlin is the most comprehensive.

-1
votes

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 :)