1

Environment:

  • Windows 7 Enterprise SP1
  • gcc version 6.3.0 (MinGW.org GCC-6.3.0-1)

I'm new to C language (not new to programming). I'm migrating a big program to C language. All works fine except for this.
I create this small program to reproduce the problem:

int AAA ()
{
    const char *p = "Enter to proceed.. X to Exit.. : ";
    const char *ok = "OK" ;
    char proceed1 ;
    char proceed2 ;

    printf(p) ;
    fflush(stdin) ;
    scanf("%c",&proceed1);
    if (proceed1 == 'X'){
        return 0 ; }
    sleep(3) ;
    fflush(stdin) ;
    printf("\n%s",p) ;
    scanf("%c",&proceed2);
    if (proceed2 == 'X'){
        return 0 ; }
    printf("\n%s",ok) ;

    return 0 ;
}

All worls fine, BUT if the user (wrongly) hits twice the enter key at the proceed1 then scanf of proceed2 automatically reads the newline character '\n' that remain in the buffer...

I tried all: rewind(stdin), fseek stdin, a getchar before the second prompt to bypass the problem, but let the scanf hang if the user correctly hit enter key once.
I repeat I'm new to C language. I understand the problem can be bypassed writing a scanf that does not accpet the \n alone.

But before proceeding I ask here this question:
How to completely clear the stdin buffer?

user438383
  • 5,716
  • 8
  • 28
  • 43
Maurizio
  • 41
  • 1
  • 3
  • 1
    Related: https://stackoverflow.com/questions/2979209/using-fflushstdin – Bob__ Jan 19 '22 at 14:15
  • Have you ever run a lengthy console command? They do accept and print keyboard inputs while they run. It is not a sign of poor quality. If you use enter for confirmation - don't. Use `y` or typing `yes` instead to confirm the user's intention. – Peter Krebs Jan 19 '22 at 14:22
  • "How to completely clear the stdin buffer?" --> There is no standard way to do this. Consider `stdin` as a _stream_ of data coming in at various times and not synchronized to what happens with `stdout` nor other code. Perhaps just do not save white-space and use `scanf(" %c",&proceed2);` - note added space. – chux - Reinstate Monica Jan 19 '22 at 14:39
  • It really depends on what you mean by "clear the buffer". Maybe you just mean "consume all the data that is present until a read blocks", but maybe you mean "read all data until the next non-whitespace character", or maybe you mean something else. Maybe you just mean "discard all data in the (internal) buffer, but don't read any more". Are you referring to the internal standard IO buffers or the terminal buffer, or some other caching layer? – William Pursell Jan 19 '22 at 15:03
  • 1
    There is some discussion of this issue in [question 12.26b](http://c-faq.com/stdio/stdinflush2.html) of the [C FAQ list](http://c-faq.com/). – Steve Summit Jan 19 '22 at 15:39
  • On Unix-like operating systems, setting the terminal driver modes normally has the side effect of flushing the keyboard input buffer. So reading the modes and then setting them to what they already were is one popular input-flushing technique. But, whoops, you said you're using Windows, so never mind. – Steve Summit Jan 19 '22 at 15:41
  • Second the C FAQ link. “_Keep in mind that users can become frustrated if you discard input that happened to be typed too quickly._” – Dúthomhas Jan 23 '22 at 06:07

2 Answers2

0

The C Standard says that fflush(stdin) has undefined behavior. You should not use it to flush pending input even on platforms where it seems to have the expected effect. There is no portable way to flush pending input, ie: all input that was typed before the prompt was issued.

If you just want to consume the characters typed by the user for a previous entry parsed with scanf(), up to and including the newline, you can use this simple function:

#include <stdio.h>

// Read and discard the rest of the current line of input.
// Returns EOF at end of file, otherwise returns a newline character
int flush_input(FILE *fp) {
    int c;
    while ((c = getc(fp)) != EOF && c != '\n')
        continue;
    return c;
}

If you are required to use scanf(), you can use these 2 calls:

scanf("%*[^\n]");  // read the remaining characters on the line if any.
scanf("%1[\n]");   // read the newline character if present.

Note however that you should not combine these 2 as a single call to scanf() because the first conversion will fail if the pending byte is a newline, and this newline will not be consumed by the second conversion.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
0

Just because this is a common enough question, notwithstanding the links to other like questions and/or duplicates, I think this bears repeating:

If you are trying to fflush(stdin), you are almost certainly doing something wrong

An input buffer flush says, “I don’t care what you typed so far. I’m throwing it away and we’re starting over.”

Such behavior is exceedingly frustrating to users of a program. Users expect their inputs to be recognized and acted on — even if those inputs are wrong! By throwing it away, your program tells the user that their wishes are not respected, leaving them feeling like they have less control of their computer than they should.

It’s an XY problem

The usual suspect behind desires to clear the input is a scanf failure. The user typed something wrong, input is stuck with invalid characters, and the program needs to get past that.

But the program does not need to get any further than the next newline, which is to say, it only needs to discard stuff that was input before the user pressed Enter. This comes from one very simple principle:

The user will always press Enter after every prompted input

The solution comes in three forms:

  1. Crash and burn. This is a strong signal that the user needs to fix his input to work, and actually provides him (or her) with a greater sense of control: the program does only what it is supposed to, accepting only input it should, and does nothing unless the input is correct!

    This also affords the user greater control in how he or she uses your program, because he can now supply prepared input to the program without having to babysit the keyboard.

  2. Skip to the EOL and ask for the user to try again. This presupposes a human is frobbing the keyboard. But if you are going to go through this grief, then:

  3. Read all input as a string, one line at a time, and endeavor to process that string (using sscanf, for example). This keeps your input and processing pointers all lined up and you will never have need to try to resynchronize with the input buffer.

Obviously, my opinion is that crash and burn is the most correct method for your usual console programs.

This is, in fact, part of the core design of Unix. This behavior enables individual utility programs to combine to perform useful scripted tasks.

The exception is a TUI program.

A text-based user interface is for a program that requires a human to operate it*, and takes over the terminal to behave in a different way, not requiring the user to press Enter to receive and act on input.

Games and text editors are examples of this kind of program.

* It is still possible to automate a TUI, of course, but the point is that the expected interaction with the program is no longer via newline-delimited streams, but rather via direct, individual key presses as input.

Dúthomhas
  • 8,200
  • 2
  • 17
  • 39