1

I was trying to solve the newline buffer problem when using fgets() after scanf() and my initial solution was to consume the newline with scanf("\n"); and it solved the problem, but then i encountred a different solution to consume the newline in buffer with scanf("%c ",&ch);

note: there is a space after %c that did solve the problem

this is the complete code that results a newline buffer problem:

#include "stdio.h"

int main() {
    char ch;
    char s[100];
    char sen[100];

    scanf("%c", &ch);
    scanf("%s", s);
    fgets(sen, sizeof(sen), stdin);

    printf("%c\n", ch);
    printf("%s\n", s);
    puts(sen);
    return 0;
}

But the same code will work when adding space after %c and %s as follows:

#include "stdio.h"

int main() {
    char ch;
    char s[100];
    char sen[100];

    scanf("%c ", &ch);
    scanf("%s ", s);
    fgets(sen, sizeof(sen), stdin);

    printf("%c\n", ch);
    printf("%s\n", s);
    puts(sen);
    return 0;
}

My question is how did the two spaces after %c and %s did consume the newline to make it possible for fgets() to take the input?

  • It is by design: that is the purpose of a space in the `scanf` format string - to consume *any amount* of whitespace in the input stream, although some specifiers do it automatically. Every character in that string is a part of the format. Please see [scanf() leaves the newline char in the buffer](https://stackoverflow.com/questions/5240789/scanf-leaves-the-new-line-char-in-the-buffer). Generally though, it is a bad idea to put any whitespace at the end of the format string, because it is only satisfied if there is another non-whitespace character in the input stream, which will remain there. – Weather Vane Jun 22 '21 at 17:25
  • 2
    The best way to "solve" this problem is to *not mix scanf and fgets*. They really do not play well together. – Steve Summit Jun 22 '21 at 17:26
  • If you simply must mix the two, the usual solution is to use inline code, or perhaps a centralized "gobble" function, to read characters up to (and including) a newline, and insert this code/call where necessary, which is generally after a `scanf` call and before a `fgets` call. – Steve Summit Jun 22 '21 at 17:28
  • 2
    For assistance in not using `scanf`, see [this question](https://stackoverflow.com/questions/58403537/). – Steve Summit Jun 22 '21 at 17:29
  • I dislike those post-fix kludges though. The newline must be there, but `scanf` is tolerant of there not being one (for example several specifiers on separate `scanf` calls can be satisfied from a single line of input). – Weather Vane Jun 22 '21 at 17:29
  • For ordinary usage, you almost always want a space *before* a `c` conversion, that is, `" %c"`. – Steve Summit Jun 22 '21 at 17:31
  • If you want to be able to enter a string with spaces, consider the `%99[^\n]` specifier (always limit string input length). – Weather Vane Jun 22 '21 at 17:34
  • 2
    @WeatherVane I would say that if you want to be able to enter a string with spaces, it's time to stop using `scanf`, and [start using `fgets` for everything](https://stackoverflow.com/questions/58403537/). `scanf`'s only virtue is that it's simple'n'easy for beginners to use. But `%[]` is the opposite of simple'n'easy. – Steve Summit Jun 22 '21 at 17:36
  • @SteveSummit I quite agree with that, but a rounded picture doesn't go amiss. – Weather Vane Jun 22 '21 at 17:38
  • `fgets` for everything is *not* the right solution. When `fgetc` doesn't work, use `fread`. – William Pursell Jun 22 '21 at 18:56
  • @WilliamPursell Is there a reason you prefer `fgetc` over `getc`? – Steve Summit Jun 22 '21 at 19:01
  • @SteveSummit No, I just like the symmetry of the names `fgetc` and `fgets`, so I usually refer to `fgetc` when contrasting it with `fgets`. – William Pursell Jun 22 '21 at 19:44

2 Answers2

1

Here are your first three scanf calls, annotated:

scanf("%c", &ch);                // reads one character, as desired (but leaves newline)
scanf("%s", s);                  // skips newline, reads word as desired (but leaves newline)
fgets(sen, sizeof(sen), stdin);  // reads preceding newline, does not read next line

scanf("%s") skips the preceding newline because %s (like most but not all scanf specifiers) always automatically skips leading whitespace (including newlines).

Here are your next three scanf calls, similarly annotated:

scanf("%c ", &ch);                // reads one character, then skips whitespace (i.e. newline)
scanf("%s ", s);                  // reads one word, then skips whitespace (i.e. newline)
fgets(sen, sizeof(sen), stdin);   // reads third line, as desired

So it might seem like the trailing space has "worked", in that the fgets call now reads the third line, as desired. But what if you add some prompts, to make the program slightly more realistic and convenient, like this:

printf("enter character: "); fflush(stdout);
scanf("%c ", &ch);
printf("enter word: "); fflush(stdout);
scanf("%s ", s);
printf("enter string: "); fflush(stdout);
fgets(sen, sizeof(sen), stdin);

When I ran it, it looked like this:

enter character: x
abc
enter word: this is a test
enter string: x
abc
this is a test

I had to enter the word abc before it prompted me for it, and similarly I had to enter the string this is a test before it prompted me for it.

So a trailing space really isn't a very good solution to the gobble-a-newline problem, because as Weather Vane explained in a comment, "it is only satisfied if there is another non-whitespace character in the input stream". That is, it causes each call to block until the beginning of the next input is seen, which totally screws up the sequencing of the prompts.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
0

If I understood correctly you are trying to remove the '\n' when you are getting an input if so try this:

fgets(input, <size>, stdin);
input[strcspn(input, "\n")] = 0;
shalevB
  • 11
  • 3