Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function fgets skips user input?

Tags:

c

When I use the function fgets, the program skips the user input, effecting the rest of the program. An example program with this effect is:

#include <stdio.h>

int main() {
    char firstDigit[2];
    char secondDigit[2];

    printf("Enter your first digit: ");
    fgets(firstDigit, 1, stdin);
    printf("\nEnter your second digit: ");
    fgets(secondDigit, 1, stdin);
    printf("\n\nYour first digit is %s and your second digit is %s.\n", firstDigit, secondDigit);
}

I then thought that maybe the problem was that fgets might be writing the newline, so I changed the code to account for that:

#include <stdio.h>

int main() {
    char firstDigit[3];
    char secondDigit[3];

    printf("Enter your first digit: ");
    fgets(firstDigit, 2, stdin);
    printf("\nEnter your second digit: ");
    fgets(secondDigit, 2, stdin);
    printf("\n\nYour first digit is %c and your second digit is %c.\n", firstDigit[0], secondDigit[0]);
}

This time, the first input works properly, but the second input is skipped.

What am I doing incorrectly?

like image 425
ericw31415 Avatar asked Aug 11 '17 22:08

ericw31415


1 Answers

char firstDigit[2] and char secondDigit[2] are not large enough to hold a digit, a newline character, and a null-terminator:

char firstDigit[3];
char secondDigit[3];

Then, the calls to fgets() need to specify the size of the buffer arrays:

fgets(firstDigit, sizeof firstDigit, stdin);
/* ... */
fgets(secondDigit, sizeof secondDigit, stdin);

When instead fgets(firstDigit, 2, stdin); is used, fgets() stores at most two characters, including the \0 character, in firstDigit[]. This means that the \n character is still in the input stream, and this interferes with the second call to fgets().

In answer to OP's comment, How would you remove the unread characters from the input stream?, a good start would be to use more generous allocations for firstDigit[] and secondDigit[]. For example, char firstDigit[100], or even char firstDigit[1000] will be large enough that any expected input will be taken in by fgets(), leaving no characters behind in the input stream. To be more certain that the input stream is empty, a portable solution is to use the idiomatic loop:

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

Note here that it is necessary to check for EOF, since getchar() may return this value if the user signals end-of-file from the keyboard, or if stdin has been redirected, or in the unlikely event of an input error. But also note that this loop should only be used if there is at least a \n character still in the input stream. Before attempting to clear the input stream with this method, the input buffer should be checked for a newline; if it is present in the buffer, the input stream is empty and the loop should not be executed. In the code below, strchr() is used to check for the newline character. This function returns a null pointer if the sought-for character is not found in the input string.

#include <stdio.h>
#include <string.h>           // for strchr()

int main(void)
{
    char firstDigit[3];       // more generous allocations would also be good
    char secondDigit[3];      // e.g., char firstDigit[1000];

    printf("Enter your first digit: ");
    fgets(firstDigit, sizeof firstDigit, stdin);

    /* Clear input stream if not empty */
    if (strchr(firstDigit, '\n') == NULL) {
        int c;
        while ((c = getchar()) != '\n' && c != EOF) {
            continue;
        }
    }

    putchar('\n');
    printf("Enter your second digit: ");
    fgets(secondDigit, sizeof secondDigit, stdin);

    /* Clear input stream if not empty */
    if (strchr(secondDigit, '\n') == NULL) {
        int c;
        while ((c = getchar()) != '\n' && c != EOF) {
            continue;
        }
    }

    puts("\n");
    printf("Your first digit is %c and your second digit is %c.\n",
           firstDigit[0], secondDigit[0]);

    return 0;
}

It may be even better to use a single buffer[] to store lines of input, and then to store individual characters in chars. You could also write a function to clear the input stream, instead of rewriting the same loop each time it is needed:

#include <stdio.h>
#include <string.h>           // for strchr()

void clear_stdin(void);

int main(void)
{
    char buffer[1000];
    char firstDigit;
    char secondDigit;

    printf("Enter your first digit: ");
    fgets(buffer, sizeof buffer, stdin);
    firstDigit = buffer[0];

    /* Clear input stream if not empty */
    if (strchr(buffer, '\n') == NULL) {
        clear_stdin();
    }

    putchar('\n');
    printf("Enter your second digit: ");
    fgets(buffer, sizeof buffer, stdin);
    secondDigit = buffer[0];

    /* Clear input stream if not empty */
    if (strchr(buffer, '\n') == NULL) {
        clear_stdin();
    }

    puts("\n");
    printf("Your first digit is %c and your second digit is %c.\n",
           firstDigit, secondDigit);

    return 0;
}

void clear_stdin(void)
{
    int c;
    while ((c = getchar()) != '\n' && c != EOF) {
        continue;
    }
}
like image 97
ad absurdum Avatar answered Nov 02 '22 01:11

ad absurdum