3

I am trying to write a program in c wherein I can control the while loop execution through user input from stdin. I have done it successfully through scanf and getchar functions. Now I am trying to replicate this using the fgets() function which is widely recommended to use rather than scanf() function. I wrote the following code:

#include<stdio.h>
#include<string.h>

int main()
{
 char loop[4]= "yes";

 printf("%s\n",loop);
 while(strcmp(loop,"yes")==0)
    {
     printf("Do you want to continue? [yes|no]: ");
     fgets(loop,4,stdin);
    }
}

In the output in terminal, I get the following:

Do you want to continue? [yes|no]: yes
Do you want to continue? [yes|no]: 

I get prompt for continuing the loop, when I type 'no', it stops as it should do, but as soon as I type 'yes', the loop executes one time and then stops.

I guess that the problem is that as soon as I press enter, fgets() stores this into loop variable, and that is why the while loop terminates. Am I thinking in right direction? If yes, how can I get rid of this extra character which is "Enter" in this case.

Pankaj
  • 519
  • 2
  • 5
  • 20
  • 5
    Try read *five* characters instead. Remember that [`fgets`](http://en.cppreference.com/w/c/io/fgets) want to read the newline as well, and with only four characters there's no space for it. – Some programmer dude May 27 '17 at 10:28
  • 3
    You'll have to [remove the trailing newline](https://stackoverflow.com/a/28462221/4142924) after calling `fgets`. – Weather Vane May 27 '17 at 10:28
  • 5
    Note that by using `fgets(loop, 4, stdin);`, the newline is left in the input stream when `"yes"` is entered, so the next input call picks up this newline character. – ad absurdum May 27 '17 at 10:35
  • Thanks everyone, especially @xing, your suggestions are very clear and I understood the weird behavior, I was getting. – Pankaj May 27 '17 at 10:56
  • 1
    After a call to fgets( ) , you can search the '\n' with strchr( ), if the '\n' was read it means the input buffer is clean, otherwise you may want to "clean the buffer". – Gam May 27 '17 at 11:28

1 Answers1

2

Change this:

fgets(loop, 4, stdin);

to this:

fgets(loop, 5, stdin);

after, of course, setting the size of your buffer to 5, like this char loop[5]= "yes";, in order to store the 3 characters of the word "yes", the newline fgets() will read (since it's reading lines, right?), and the string NULL-terminator (as you already know).


To explain the behavior of your code, you have to understand @DavidBowling's comment:

Note that by using fgets(loop, 4, stdin);, the newline is left in the input stream when "yes" is entered, so the next input call picks up this newline character.

Use this program to demonstrate that:

#include<stdio.h>
#include<string.h>

int main()
{
  char loop[4]= "yes";

  while(strcmp(loop,"yes")==0)
  {
    printf("Do you want to continue? [yes|no]: ");
    fgets(loop, 4, stdin);
    printf("|%s|\n",loop);    // I surrounded the output, in order to catch the newline
  }
  return 0;
}

Output:

Do you want to continue? [yes|no]: yes
|yes|
Do you want to continue? [yes|no]: |
|

which shows that the newline character is left in the Standard Input (STDIN) buffer, and gets consumed with the second call to fgets(). From the reference of the method:

Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or the end-of-file is reached, whichever happens first.

A terminating null character is automatically appended after the characters copied to str.

Let's see what happens, step-by-step:

You type yes, and then hit Enter, which makes the Standard Input buffer look like this:

------------------
| y | e | s | \n |
------------------

and now the first call to fgets(loop, 4, stdin); gets executed, which means that the method will try to read 3 (4 - 1) characters from STDIN. It reads y, e and s, moves them from STDIN's buffer to your program's buffer you declared, named loop, and then appends the string NULL terminator.

Now the STDIN buffer is:

------------------
| \n |   |   |   |
------------------

and before the user has a chance to input, fgets(loop, 4, stdin); is going to be executed (it's the method's duty to do so, since they are data waiting in STDIN's buffer to be consumed - if there were no data, then the method would patiently wait for the user to input something...).

Now, it copies the newline character into loop, stops there, since now the STDIN buffer is empty, and eventually appends a string NULL terminator to loop (at index 1).

and now the STDIN buffer is empty:

-----------------
|   |   |   |   |
-----------------

since there was no more input by the user - the code's flow moved after the while loop, since the loop's condition evaluated to false, when loop was a string with the newline character as its first character.


PS: Removing trailing newline character from fgets() input.

gsamaras
  • 71,951
  • 46
  • 188
  • 305