Fgets skip first character

97 Views Asked by At

When I take user’s name as input and use that information to do another task, the output does not contains first character of the first student name.

int main() {
    // Task1 - Enter student number
    int i, number;
    printf("Enter the number of students: ");
    scanf("%d", &number);
    getchar();
    while (number > 1000) {
        printf("Your number is too high. Maximum number of student is 1000. Please enter again: ");
        scanf("%d", &number);
    }
    //Task2 - get student info
    struct studentInfo students[number];
   
    for (i = 0; i < number; i++) {
        printf("\nEnter data for student %d:\n", i + 1);
        printf("Enter student %d name: ", i + 1);
        getchar(); // consume the newline character left in the input stream
        fgets(students[i].fullName, sizeof(students[i].fullName), stdin); //use fgets instead of scanf
        students[i].fullName[strcspn(students[i].fullName, "\n")] = 0; // Remove trailing newline
 
        printf("Enter student %d ID: ", i + 1);
        scanf("%s", students[i].ID);
       
        printf("Enter student %d birthdate: ", i + 1);
        scanf("%s", students[i].birthDate);
    }
}

I have changed fgets to gets and add getchar but it’s still not working

2

There are 2 best solutions below

7
Joshua On

The problem is after scanf("%d", ...), a newline is left in the input stream. Use of scanf() for keyboard input is a bad idea. Consider using fgets() and sscanf() instead.

Example:

    printf("Enter the number of students: ");
    scanf("%d", &number);

would become:

    printf("Enter the number of students: ");
    fgets(keyboard_line, 80, stdin);
    sscanf(keyboard_line, "%d", &number);

Where you declare keyboard_line as char keyboard_line[80]; at the top of main();

1
Andreas Wenzel On

The problem is that the statement

scanf("%d", &number);

will leave the newline character on the input stream, whereas the statement

fgets(students[i].fullName, sizeof(students[i].fullName), stdin);

will read an entire line of input including the newline character (assuming that the supplied memory buffer is large enough to store the entire line).

You appear to be attempting to compensate for the fact that scanf leaves the newline character on the input stream by using the statement

getchar();

in several places in your code to read and discard the newline character. However, you are not doing this correctly.

If the user enters a number below 1000 for the statement

scanf("%d", &number);

then you will execute the statement

getchar();

twice afterwards, because that statement is in two places in your posted code. This means that it will probably read the newline character as well as the first character of the next line of input of the user.

This explains why in the first loop iteration, students[i].fullName is missing the first character.

In order to fix this, I suggest that you always have the statement

getchar();

either

  1. immediately after every scanf function call, or
  2. immediately before every fgets function call.

The problem in your posted code is that you are doing a mixture of both.

Note that solution #2 will only work if the first statement in the program is a scanf statement, because if the first statement is a fgets statement, then there will be no newline character to remove. For this reason, it would probably be better to use solution #1.

Also, using the statement

getchar();

will only work if scanf left over only a newline character. However, if the user for example enters

6abc

then the %d format specifier of scanf will only match 6, but will leave abc\n on the input stream. In that case, it would be necessary to call getchar() four times instead of only once.

For this reason, it is generally better to use the following loop instead:

int c;

do
{
    c = getchar();

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

This loop can be shortened to the following two lines:

for ( int c; ( c = getchar() ) != EOF && c != '\n' )
    ;

If you use this several times in your program, it may be easier to simply write your own function:

void discard_remainder_of_line()
{
    int c;

    do
    {
        c = getchar();

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

That way, you only have to write

discard_remainder_of_line();

after every scanf statement.

It would also be safer to always check the return value of scanf before using the result of scanf.