C: why doesn't fgets function work? [duplicate] - c

This question already has answers here:
scanf() leaves the newline character in the buffer
(7 answers)
Closed 5 years ago.
I'm writing a program to create a linked list node. After that, i add some functions (insert,search,update,and print). The linked list contains number, name, and quantity on hand.
The main function prompts users to enter an operation code, then calls a function requested actions.
main()
{
char code;
int c;
for(;;)
{
printf("i: insert\ns: search\nu: update\np: print\n");
printf("Enter operation code: ");
scanf("%c",&code);
while((c=getchar() )!= '\n'&& c!=EOF);
switch(code)
{
case 'i': insert();
break;
case 's': search();
break;
case 'u': update();
break;
case 'p': print();
break;
case 'q':return 0;
default: printf("Illegal code\n");
break;
}
printf("\n");
}
}
All function work correctly. However, in the insert function, I use fgets statement to get the string input from the user. (NAME_LEN = 25)
void insert()
{
node *previous,*current,*new_node;
new_node = malloc(sizeof(node));
printf("Enter part number: ");
scanf("%d",&new_node->number);
for(current = inventory,previous=NULL;
current != NULL&& new_node->number > current->number;
previous = current,current = current -> next);
if(current != NULL && new_node ->number == current->number)
{
printf("Part already exists.\n");
free(new_node);
return;
}
printf("Enter name part: ");
fgets(new_node->Name,NAME_LEN+1,stdin); // i use fgets to input string name
printf("Enter quantity on hand: ");
scanf("%d",&new_node->on_hand);
new_node -> next = current;
// move to the next node
if(previous == NULL)
inventory =new_node;
else
previous->next = new_node;
}
Unfortunately, this code doesn't work. The program shows that
i: insert
s: search
u: update
p: print
Enter operation code: i
Enter part number: 2
Enter name part: Enter quantity on hand: 3
As you can see that, the name part was missed.
Moreover, after inserting a new node, the program automatically shows the default case in the switch.
i: insert
s: search
u: update
p: print
Enter operation code: i
Enter part number: 2
Enter name part: Enter quantity on hand: 3
i: insert
s: search
u: update
p: print
Enter operation code: Illegal code
Can you explain to me what happens, pls?.

Well the thing is fgets is consuming the \n that is left in stdin from previous input. Easy way would be to use dummy getchar() or using fgets until you get a non-whitespace input.
Do one thing, after scanf put a getchar().
scanf("%c",&code);
getchar();
^^^^
Will consume the `\n`.
To give you a more clear explanation suppose you enter iEnter
Then i is stored into the variable code. But what about the \n. fgets() when starts reading input from stdin finds that \n and it stops. By using the getchar() you have consumed the \n. Now all non-whitespace characters will be consumed by fgets until it finds a \n or EOF or buffer gets full.
The most portable and clean way to flush the stdin would be (To be correct the stdin must have atleast the \n character otherwise it will go on eating other charcaters as well (maybe your valid inputs)).
int c;
while ((c = getchar()) != '\n' && c != EOF);
This you can use whenever you encounter some garbage input or clear the stdin.
Also to really understand whether fgets function works or not you need to check what it returns. Does it return NULL? If not then check it and then try to understand. Simply because you didn't see any output when printed the buffer doesn't mean fgets failed.
Edit
The question at first posted mentioned that there is fgets right after the scanf("%c",&code). Here it is clear on the edit that scanf("%d",&new_node->number); is there before fgets but that doesn't change the scenario. Now the \n after the entered number (you input 2ENTER) is still there and fgets consumes it. So we need to place dummy getchar() over there or stdin flushing technique shown above.
scanf("%d",&new_node->number);
getchar();
^^^^
This consumes the stray `\n`

Related

What is the purpose of this dummy variable variable and why program exits without it in c? [duplicate]

This question already has answers here:
scanf() leaves the newline character in the buffer
(7 answers)
Closed last month.
int main ()
{
char c;
int choice,dummy;
do{
printf("1. Print Hello\n2. Print World\n3. Exit\n");
scanf("%d",&choice);
switch(choice)
{
case 1 :
printf("Hello\n");
break;
case 2:
printf("World\n");
break;
case 3:
exit(0);
break;
default:
printf("please enter valid choice\n5");
}
printf("do you want to enter more?");
scanf("%d",&dummy);
scanf("%c",&c);
}while(c=='y');
}
tried removing int dummy variable and dummy input, program exits without taking any character input. how is this helping the code to not to exit ?
Whoever wrote this doesn't understand how scanf format specifiers work.
The first call to scanf uses the %d format specifier. This reads and discards any leading whitespace, then reads a decimal integer. Assuming you pressed ENTER after typing in this integer, a newline character will be left in the input buffer.
The %c format specifier reads the first character in the input buffer but does not strip off trailing whitespace. So without the prior call reading dummy, this will read the newline that was stuck in the input buffer previously into c. That causes the comparison c=='y' to be false so the loop exits..
The extra call to scanf for dummy reads and discards the newline left in the input buffer and wait for an integer to be read. Presumably, the user will enter y or n given the prompt, so that call to scanf will not read anything more and will return 0, indicating that nothing matched. The following scanf then reads the y or n.
The proper way to handle this is to add a space before the %c format specifier to absorb leading whitespace instead of adding a "dummy" call:
printf("do you want to enter more?");
scanf(" %c",&c);
The scanf("%d",&dummy) removes a newline so the subsequent scanf("%c",&c); works. Otherwise, it will take \n as its character.
The "extra" newline comes from the fact that it is left in the input stream by the original: scanf("%d",&choice);
To fix, remove the dummy related code and do:
scanf(" %c",&c);
Note the preceding space in the format. This tells scanf to skip over whitespace (which includes newlines).
The fully corrected code is:
#include <stdio.h>
#include <stdlib.h>
int
main(void)
{
char c;
int choice;
do {
printf("1. Print Hello\n2. Print World\n3. Exit\n");
scanf("%d", &choice);
switch (choice) {
case 1:
printf("Hello\n");
break;
case 2:
printf("World\n");
break;
case 3:
exit(0);
break;
default:
printf("please enter valid choice\n5");
}
printf("do you want to enter more?");
scanf(" %c", &c);
} while (c == 'y');
return 0;
}

Program is not waiting for user to take input from user inside while(1) loop [duplicate]

This question already has answers here:
scanf() leaves the newline character in the buffer
(7 answers)
Closed 4 years ago.
I am implementing a Stack data structure using an array. To perform an operation on a stack like Push or Pop or Exit from Program, I am taking an input from a user in the form of an integer(Say 1 for Push).
For taking an input from a user continuously, I am running a while(1) loop and inside a loop asking a user to give an integer as an input for required operation.
Problem: during the execution, the program is not waiting for a user to give input and goes into infinite loop. I have tried to clear stdin using fflush but still not working.
Please look at the code below with given comments and help.
Thanks.
//Main function
int main(){
STACK marks, roll_number; //marks and roll_number are two stacks of type STACK
//Initialize both Stacks with required size
initStack(&marks, 10);
initStack(&roll_number, 10);
int choice, marks_data;
//Menu for user to choose from available operations
printf("1. Push\n");
printf("2. Pop\n");
printf("3. Exit\n");
//Taking user input for operations
while(1){
printf("Enter choice of Operation:\n");
//Clearing standard input. Although not a best practice but implementing to
//take user input
fflush(stdin);
// program is not stopping here and taking invalid choice
//hence executing 'default statement' of switch case
scanf("%d", &choice);
switch(choice){
case 1: printf("Enter Marks");
scanf(" %d", &marks_data);
push(&marks, marks_data);
break;
case 2: marks_data = pop(&marks);
printf("Deleted Data : %d\n", marks_data);
break;
case 3: exit(0);
default: printf("Invalid Choice of Operation !!\n");
}
//using to clear \n character from user and taking valid input
printf("Press Enter Key to continue...");
while(getchar() != '\n')
getchar();
}
return 0;
}
When you use scanf, you have to put your "account's hat" on and account for all characters either read from the input buffer, or left unread in the input buffer. This is especially true when mixing input between numeric and character input or when mixing input functions in your code.
In your case, you call scanf ("%d", ...) for choice and marks, but then attempt to control your loop execution with:
printf("Press Enter Key to continue...");
while(getchar() != '\n')
getchar();
As a good scanf accountant, you know reading using the '%d' format specifier will read digits from stdin up to the first non-digit character and terminate the read, leaving the non-digit character in stdin unread. (presuming no stray characters were entered following choice or mark), that will be the '\n' character generated by pressing Enter following your previous input.
When you test while(getchar() != '\n'), the first character read is likely the '\n' character, causing your loop to test TRUE and skip the getchar() call within the loop.
The easiest solution is just to add an additional getchar() below your current one.
Next fflush(stdin) is Undefined Behavior on just about all systems except windows. On Linux, fflush(stdin) is defined, but only for seekable streams -- which only applies to stdin if a file has been redirected to your program on stdin, e.g.
./yourexe < somefile.txt
Otherwise fflush(stdin) is not defined. So you have to ask yourself -- being non-portable on everything but windows unless stdin is seekable as the result of redirection -- "Is is really good practice to use anywhere?" and "How do I force the user to redirect a file to stdin?" (you can't). So best to avoid all the way around.
Look things over and let me know if you have further questions.

Unclear about stdin input issue

I write a piece of code to help myself understand how things work when stdin and stdout involved.
Here is my code:
#include<stdio.h>
#include<stdlib.h>
void prompt(){
int i=0;
printf("please select:\n"); //string1
printf("1.input\n2.print\n3.Exit\n"); //string2
scanf("%d",&i);
switch(i){
case 1:
printf("Data input!\n");
break;
case 2:
printf("data printed!\n");
break;
case 3:
exit(0);
case 10:
printf("Newline detected!"); //string3
default:
printf("Please input a valid number!(1-3)"); //string4
}
}
int main()
{
while(1)
prompt();
return 0;
}
What I expect this code to do is:
prompt me for input;
then I enter a number 4,which is out of the cases;
so the default case will be matched and the string 'Please input a valid...'(string 4) will be printed.
as there is still a newline character left in the stdin buffer, in the next loop,
the variable 'i' will automatically get a newline character, which is 10 in ACSII
So after printing out 'please select..1.input\n2.print....',, a string 'Newline detected!'(string 3) will be immediately printed out.
And then the code goes into the third loop, and prompt me for input....
But that never happen. I cannot see any 'Newline detected!" in the output even if I enter a number 4, which is out of the cases.
Anyone can elaborate how this snippet work exactly?
By the way: I originally assume that when there is something printed in the stdout, the stdin buffer will be flushed automatically. But some fact proved me wrong. Is my assumption true or false?
Also, when I enter a character(for example, a g) rather than a number, I got string 1, string 2, sring 4 printed in the screen infinitly. Why is that?
#######################################################################3
Edit: After viewing the answer, I make another snippet, to help understand.
#include<stdio.h>
#include<stdlib.h>
void output();
int main()
{
int i;
printf("Enter a number here:\n");
scanf("%d",&i);
output();
return 0;
}
void output(){
char a;
if (scanf("%c",&a)!=1){
printf("scanf error!!");
exit(1);
}
switch(a){
case 'a':
printf("an char a is entered");
break;
case 'b':
printf("an char b is entered");
break;
default:
printf("%c",a);
printf("other thing is entered");
}
}
whatever you enter the first time the program prompt you, you will never get your second prompt. For example, when the program prompt you for the first time, if you enter a number 4, then you will get a newline and a string "other thing is entered" printed on you screen. Why is that?
as there is still a newline character left in the stdin buffer, in the next loop, the variable 'i' will automatically get a newline character, which is 10 in ACSII
So after printing out 'please select..1.input\n2.print....',, a string 'Newline detected!'(string 3) will be immediately printed out.
That is not correct. When you use
scanf("%d",&i);
all white spaces are ignored, which includes the newline.
And then the code goes into the third loop, and prompt me for input....
Now you know it stays in the second loop, waiting for a number to be entered.
I originally assume that when there is something printed in the stdout, the stdin buffer will be flushed automatically. But some fact proved me wrong. Is my assumption true or false?
That assumption is false. stdout is flushed when you wait for input from stdin.
Also, when I enter a character(for example, a g) rather than a number, I got string 1, string 2, sring 4 printed in the screen infinitly. Why is that?
That's because the program is not able to read the character when it executes the line:
scanf("%d",&i);
The character stays in the input stream. You don't have any code to remove that character from the input stream. That makes the program stay in an infinite loop.
as there is still a newline character left in the stdin buffer, in the
next loop, the variable 'i' will automatically get a newline
character, which is 10 in ACSII
Wrong. %d will get a integer until a newline or a space is encountered and the newline character in the buffer will not be consumed by the next scanf()
So always check the return value of scanf()
if(scanf("%d",&i) != 1)
{
printf("scanf failed\n");
return 1;
}
As a side note:
case 10:
printf("Newline detected!");
There is a a break missing in this case.
Addressing the second program in the question, here's a mildly revised version. It prints more information, and uses strict prototypes.
#include <stdio.h>
#include <stdlib.h>
void output(void);
int main(void)
{
int i;
printf("Enter a number here:\n");
if (scanf("%d", &i) == 1)
printf("Read %d OK\n", i);
output();
return 0;
}
void output(void)
{
char a;
if (scanf("%c", &a) != 1)
{
printf("scanf error!!");
exit(1);
}
printf("Character %d (%c) entered\n", a, a);
switch (a)
{
case 'a':
printf("an char a is entered\n");
break;
case 'b':
printf("an char b is entered\n");
break;
default:
printf("%c", a);
printf("other thing is entered\n");
break;
}
}
Example runs:
$ ./stdin
Enter a number here:
23499911
Read 23499911 OK
Character 10 (
) entered
other thing is entered
$ ./stdin
Enter a number here:
2A
Read 2 OK
Character 65 (A) entered
Aother thing is entered
$ ./stdin
Enter a number here:
19b
Read 19 OK
Character 98 (b) entered
an char b is entered
$ ./stdin
Enter a number here:
999a
Read 999 OK
Character 97 (a) entered
an char a is entered
$
Note that in the first run, the stray character is the newline after the second 1 digit, character code 10. This is what you should get.
Do make sure your output print operations end with a newline (so that it appears in a timely manner). If you don't, your output may be held up indefinitely.
Do you mean that the scanf() function will not strip the newline character in the stdin buffer?
The scanf("%d", &i) certainly doesn't. The scanf("%c", &a) does. When scanf() completes a conversion, it puts the character that is not part of the conversion back into the input stream ready for the next input operation. So, it doesn't matter whether there's a space, a letter or a newline after the number, that character is left ready for the next input operation to read it. Most scanf() operations skip leading white space. There are three exceptions: %c, %n and %[…] (scansets). They do not skip leading white space.

do while loop for Yes/No user prompt

My program which finds prime factors is all set...the only thing left that I need to do is this type of output:
Do you want to try another number? Say Y(es) or N(o): y
//asks for another number (goes through the program again)
Do you want to try another number? Say Y(es) or N(o): n
//Thank you for using my program. Good Bye!
I have my attempt at this below...When I type n it does the correct output. But if I type 'y' it just says the same thing n does....How can I loop the entire program without putting the code for the program inside this while loop I have? So when I press y it goes through the program again?
int main() {
unsigned num;
char response;
do{
printf("Please enter a positive integer greater than 1 and less than 2000: ");
scanf("%d", &num);
if (num > 1 && num < 2000){
printf("The distinctive prime facters are given below: \n");
printDistinctPrimeFactors(num);
printf("All of the prime factors are given below: \n");
printPrimeFactors(num);
}
else{
printf("Sorry that number does not fall within the given range. \n");
}
printf("Do you want to try another number? Say Y(es) or N(o): \n");
response = getchar();
getchar();
}
while(response == 'Y' || response == 'y');
printf("Thank you for using my program. Goodbye!");
return 0;
} /* main() */
The problem is probably, that you're getting something that isn't y from getchar and the loop exits, as the condition is not matched.
getchar() may use a buffer, so when you type 'y' and hit enter, you will get char 121 (y) and 10 (enter).
Try the following progam and see what output you get:
#include <stdio.h>
int main(void) {
char c = 0;
while((c=getchar())) {
printf("%d\n", c);
}
return 0;
}
You will see something like this:
$ ./getchar
f<hit enter>
102
10
What you can see is that the keyboard input is buffered and with the next run of getchar() you get the buffered newline.
EDIT: My description is only partially correct in terms of your problem. You use scanf to read the number you're testing against. So you do: number, enter, y, enter.
scanf reads the number, leaves the newline from your enter in the buffer, the response = getchar(); reads the newline and stores the newline in response, the next call to getchar() (to strip the newline I described above) gets the 'y' and your loop exits.
You can fix this by having scanf read the newline, so it doesn't linger in the buffer: scanf("%d\n", &number);.
When reading input using scanf (when you enter your number above), the input is read after the return key is pressed but the newline generated by the return key is not consumed by scanf.
That means your first call to getchar() will return the newline (still sitting in the buffer), which is not a 'Y'.
If you reverse your two calls to getchar() - where the second one is the one you assign to your variable, your program will work.
printf("Do you want to try another number? Say Y(es) or N(o): \n");
getchar(); // the newline not consumed by the scanf way above
response = getchar();
just put getchar() after scanf statement of yours that will eat the unnecessary '\n' from buffer...
As others have stated, there is a single '\n' character in the input stream left over from your earlier call to scanf().
Fortunately, the standard library function fpurge(FILE *stream) erases any input or output buffered in the given stream. When placed anywhere between your calls to scanf() and getchar(), the following will rid stdin of anything left in the buffer:
fpurge(stdin);

C - user single char input rendering weird results

New here and I have a very simple question. I am making a simple program in C that requires the user to enter a choice of what to do with a char. After they enter the result, the program goes back to the menu. However it seems to take some sort of ghost input as if the char has some unknown value. I need to set the char back to its default state.
Code:
/* display menu for user */
void menu() {
printf("\n- - - Phone Book Database - - -\n");
printf("\nSelect an action:\n\n");
printf("\tc:\tCreate a database entry.\n");
printf("\ts:\tSearch the database entries.\n");
printf("\td:\tDelete a database entry.\n");
printf("\tq:\tQuit program.\n\n");
printf("Enter choice: ");
menu_choice = getchar();
if(menu_choice != 'c' && menu_choice != 's'
&& menu_choice != 'd' && menu_choice != 'q') {
printf("\n\n\tInvalid choice.\n");
menu();
}
//fflush(stdin);
}
Here is an example output:
- - - Phone Book Database - - -
Select an action:
c: Create a database entry.
s: Search the database entries.
d: Delete a database entry.
q: Quit program.
Enter choice: c
Enter name: test
Enter address: test
Enter number: 3
- - - Phone Book Database - - -
Select an action:
c: Create a database entry.
s: Search the database entries.
d: Delete a database entry.
q: Quit program.
Enter choice:
Invalid choice.
- - - Phone Book Database - - -
Select an action:
c: Create a database entry.
s: Search the database entries.
d: Delete a database entry.
q: Quit program.
Enter choice: q
Entering c as an input calls the following function
/* creates a new record in array */
void create_record() {
char name[MAX];
char address[MAX];
int number;
rec_num++; /* add 1 to marker for record placement */
printf("\nEnter name: ");
scanf("%s", name);
printf("\nEnter address: ");
scanf("%s", address);
printf("\nEnter number: ");
scanf("%d", &number);
strcpy(record[rec_num].name, name);
strcpy(record[rec_num].address, address);
record[rec_num].number = number;
}
Looks like you have your answer already, and reviewing, it is correct; using getchar() will read one character from stdin
printf("Enter choice: ");
menu_choice = getchar();
When I get to this prompt at the console and type c<enter key> it's causing two char's to go to stdin: 'c' and '\n'
The first time getchar(); runs, it picks up just the 'c' leaving the newline character. On the second iteration the '\n' is picked up without waiting on anything from the user; thus is seems as though there is "ghost input".
I just wanted to point out, whenever you're getting input from the user and the results aren't what you've expected it doesn't hurt to just dump it back to verify what's happening, something like:
if(menu_choice != 'c' && menu_choice != 's'
&& menu_choice != 'd' && menu_choice != 'q') {
printf("\n\n\tYou entered %c (%d).\n", menu_choice, menu_choice);
printf("\tThat's an invalid choice.\n");
menu();
}
Would have shown you:
You entered
(10).
That's an invalid choice.
Between the fact it was on a different line, and checking that decimal result against an ASCII table you'd see that 1010 is the newline character, which could help lead you to a result.
Edit:
1 option for consuming a newline -
printf("what character?");
c = getchar(); // Get the character from stdin
getchar(); // consume the newline character that was left
a second option:
printf("what character?");
scanf("%c ", &c); // Get the character from stdin, note the space after the %c
// that will allow any special characters including \t and \n
If you just want to get the character and then end at & consume the '\n', you have to add and escape the newline char:
printf("what character?");
scanf("%c\\n", &c);
As others have pointed out, it's the newline character. Just add another
(void) getchar(); /* read and discard newline */
where ever you only want to read one character.
The return char '\n' is probably being left in stdin so that next time you come around it getchar() is fetching this.
Try looking at the value that is fetched with getchar() either with a debugger or just by printing it out.
It may be that getchar() isn't a great choice for your application and that scanf() would do a better job, that way all of stdin will be fetched and it should be empty next time you come around. Obviously you will need to provide scanf with more than a single char to write too, as in your other code.
You can then simply look at the first char in the array to get your char.
A stray character, almost certainly '\n', is left on your input buffer after your input commands.
To remove it, you can add:
fflush(stdin)
...right before your getchar() call.

Resources