4

So in the following bit of code I'm reading an option from the user to then decide whether to perform a given action:

printf("Do you want to execute %s: ", exe);

char input = getchar();

if (input == 'y') {
    return execute(exe);
} return 0;

The problem I'm having is that after the line char input = getchar() a character (I'm assuming an enter key or newline) gets stuck in stdin, and a future call to fgets() (reading from stdin) from my execute() function therefore eats up this stuck character and doesn't prompt the user for the required input.

Now I can define the following function to 'eat' any stray characters for me if called before calling execute()...

void iflush() {
    int c;

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

... and this solves the problem. But I'm left wondering why this is happening in the first place? Is there a more 'proper' way of getting a char from stdin without causing this stray character which then messes up my other methods? I'm very new to C and am keen to be sure I'm learning good habits.

It seems odd that I have to add a helper method, even if simple, to prevent errors in my program just from recording some user input. I've tried using fgetc() also but same problem.

transiti0nary
  • 493
  • 6
  • 25
  • Did you try `fflush(stdin);` after the statement where you call `getchar()`? – VHS Nov 16 '16 at 14:57
  • yeah that didn't do anything – transiti0nary Nov 16 '16 at 15:00
  • 5
    @VHS You know it is a really bad advice? `fflush(stdin);` is invoking undefined behavior. – Eugene Sh. Nov 16 '16 at 15:03
  • @VHS: Please read [Using `fflush(stdin)`](http://stackoverflow.com/questions/2979209/using-fflushstdin) for a more nuanced story about `fflush(stdin)` — it is defined on Windows when using the Microsoft C run time libraries, but it is otherwise not portable. – Jonathan Leffler Nov 16 '16 at 15:15
  • It is definitively not trivial to parse input (data in general), so writing such an helper (yours is broken as good chars are lost, return them when input is correct) is very common. – Jean-Baptiste Yunès Nov 16 '16 at 15:32
  • Note: Better to use `int input = getchar();` rather than `char input = getchar();`, just like code did in `iflush()`. – chux - Reinstate Monica Nov 16 '16 at 16:20

1 Answers1

4

I'm left wondering why this is happening in the first place?

It's because when you can characters with %c (or fgetc()), it doesn't ignore ay of the whitespaces in the input stream. Some format specifiers, for example %d, ignore any whitespaces left in the input stream. So, it's not a problem. But %c doesn't ignore it. Hence, you typically use those "eat" functions.

Think if you are scanning a whitespace character (such as space, newline, etc), how'd you able to scan it using scanf()? Obviously, there are different and better ways such as using fgets().

I'm very new to C and am keen to be sure I'm learning good habits.

My suggestion is to avoid scanf() completely. Instead use fgets() to read a line and then you can parse that line using sscanf(). Please read: Why does everyone say not to use scanf? What should I use instead?

One thing to be aware about fgets() is if you input buffer has enough space, then it'll read the newline character as well, which you would want to avoid in most user inputs. So, you need to check if the last character is a newline and then remove it.

You can achieve it using strcspn() function such as:

char line[256];
if (fgets(line, sizeof line, stdin) == NULL) {
    /* handle failure */
}
line[strcspn(line, "\n")] = 0; /* remove the newline char if present */

If you are mainly interested in reading just one char, then an array 3 chars would be sufficient. Still using a larger buffer doesn't hurt:

char line[256];
char ch;
if (fgets(line, sizeof line, stdin) == NULL) {
    /* handle failure */
}
if (sscanf(line, "%c", &ch) != 1) {
    /* handle failure */        
}
/* Now you can use 'ch' to check if it's 'y' */
P.P
  • 117,907
  • 20
  • 175
  • 238
  • Very nice except "reading just one char, then an array 2 chars would be sufficient". Usually to read one `char`, folks don't count the `\n`. Likely need at least 3 for the: character, line-feed and null character. Recommend buffer sizes twice what you think the maximum should be. 256 is nice for user input. – chux - Reinstate Monica Nov 16 '16 at 16:17
  • `fgets()` would only store the `\n` if there's no room in the buffer. So, 2 is sufficient: 1 char and the zero byte. So when reading one char (even if it's a newline) 2 byte buffer size is good enough. – P.P Nov 16 '16 at 16:21
  • True, yet the `'\n'` remains in `stdin` for the next input operation - something OP appears to want to avoid should a user under `"y\n"` to read just "one" character. If code needs to read one character at a time including `'\n'`, just use `fgetc()` - this handles reading the null character, which is problematic with `fgets()`. – chux - Reinstate Monica Nov 16 '16 at 16:26
  • I have modified it slightly in order to handle that. Thanks. – P.P Nov 16 '16 at 16:33
  • Using strcspn () like that is bad advice as it is possible to read lines with no trailing newline character. – Bjorn A. Nov 16 '16 at 18:01
  • @BjornA. It's not. If there's no trailing newline, it'll simply overwrite the null byte. – P.P Nov 16 '16 at 18:04
  • Usr: what happens if the input is a single char or a line longer than the buffer? – Bjorn A. Nov 16 '16 at 18:06
  • @usr, forget my bad comment. – Bjorn A. Nov 16 '16 at 18:12