gets() inside switch being ignored - c

Can someone tell me why the gets in the case 1 is being completely ignored?
// #define M 50 is at the top
char product[M] = {0};
int choice = -1;
printf("Command: ");
scanf("%d", &choice);
switch(choice){
case 0:
break;
case 1:
printf("Product: ");
gets(product);
insert_product(warehouse, price, product);
break;
case 2:
// print_all();
break;
default:
printf("Scelta non valida\n");
break;
}

The gets might be skipped by an ending char from a previous input. Try adding a getchar() before the gets to see if that is the problem.

The problem is that the newline you input when reading choice:
scanf("%d", &choice);
is being taken as input for the next gets(product); and so it reads an empty string. Try to add an artificial getchar after scanf to absorb the newline:
scanf("%d", &choice);
getchar();
Should work now.

First, the important bit...
NEVER NEVER NEVER NEVER NEVER use gets. Ever. Even for practice code. Even if someone else tells you to. It's like the movie Highlander II; it's just best to pretend it never existed. It was deprecated in C99 and has been completely removed from C2011. Don't use it. Don't try to justify using it by claiming this is just practice code. If someone else demands you use it, push back and tell them you'll use fgets instead. If they give you grief, send them to me and I'll straighten them out. With a baseball bat if necessary.
Okay, now that I've cleared that up...
gets is picking up the trailing newline from the input you entered for the previous scanf call. You have several choices:
Use another scanf call instead of gets, this time with the %s conversion specifier (along with a field width); unlike gets, it will skip over any leading newline characters;
Call getchar() (or equivalent) to consume the newline before calling gets;
Instead of reading choice as an integer using scanf, read it as text using fgets and convert it to an integer using strtol.
Option 3 is the best IMO. Mixing calls to scanf/fscanf and fgets is usually a recipe for heartburn, precisely because of the trailing newline issue. Better to stick with one or the other for everything. The advantage of using fgets to read everything as text is that it makes input validation easier, especially for numeric input.

// #define M 50 is at the top
char product[M] = {0};
int choice = -1;
printf("Command: ");
scanf("%d", &choice);
switch(choice){
case 0:
break;
case 1:
printf("Product: ");
getchar();
gets(product);
insert_product(warehouse, price, product);
break;
case 2:
// print_all();
break;
default:
printf("Scelta non valida\n");
break;
}

Use fflush(stdin) before using gets as input as the gets is taking newline character as input after the use of scanf("%d", &choice);
fflush(stdin) clears the output buffer and moves the buffered data to console.

Related

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.

how to use fgets and sscanf for integers in loop

Beginner with C here. I am trying to run a loop where strings and ints are entered into various fields of a struct. When prompted for a 'last name', the user can press enter with no other input and the loop should end.
The problem is that with this code, the loop doesnt end (last name and first name entry requests run together on the same line) and the value for salary always comes out wrong (0 or some large number)
while (employee_num <= 2)
{
printf("Enter last name ");
fgets(employee[employee_num].last_name, sizeof(employee[employee_num].last_name), stdin);
if(strlen(employee[employee_num].last_name) == 0)
break;
printf("Enter first name ");
fgets(employee[employee_num].first_name, sizeof(employee[employee_num].first_name), stdin);
printf("Enter title ");
fgets(employee[employee_num].title, sizeof(employee[employee_num].title), stdin);
printf("Enter salary ");
fgets(strng_buffer, 1, stdin);
sscanf(strng_buffer, "%d", &employee[employee_num].salary);
++employee_num;
getchar();
}
If I try this code instead, I am able to exit the loop properly after the first run through it, but cannot exit after that (by pressing enter at the last name portion - perhaps a \n I cant seem to clear?):
char strng_buffer[16];
while (employee_num <= 5)
{
printf("Enter last name ");
fgets(strng_buffer, sizeof(strng_buffer), stdin);
sscanf(strng_buffer, "%s", employee[employee_num].last_name);
if(strlen(employee[employee_num].last_name) == 0)
break;
printf("Enter first name ");
fgets(strng_buffer, sizeof(strng_buffer), stdin);
sscanf(strng_buffer, "%s", employee[employee_num].first_name);
printf("Enter title ");
fgets(strng_buffer, sizeof(strng_buffer), stdin);
sscanf(strng_buffer, "%s", employee[employee_num].title);
printf("Enter salary ");
scanf("%d", &employee[employee_num].salary);
++employee_num;
getchar();
}
I am curious as to how to make this work as intended and what best practice would be for entries like this (ie use of sscanf, fgets, etc)
Thanks in advance!
The Loop breaks prematurely when it encounters the break statement
if(strlen(strng_buffer) == 0)
break;
The uninitialized character buffer strng_buffer, coincidently has null as the first character causing strlen to return 0
I believe you may have intended
if(strlen(employee[employee_num].last_name) == 0)
break;
as the loop terminatorm, and it was a typo in your part causing premature loop exit.
Assuming the fix mentioned by Abhijit, why transform the first into the second? Are you aware that the second behaves differently to the first, because of the addition of sscanf? If your intention was to shorten the first, the second seems quite bulky. Rather than adding sscanf to the situation, why not shorten the first by declaring a struct employee *e = employee + employee_num; and using that repetitively, instead of employee[employee_num]?
One "best practise" regarding fgets is to check it's return value. What do you suppose fgets might return, if it encounters EOF? What do you suppose fgets would return if it's successful?
One "best practise" regarding scanf is to check it's return value. In regards to the return value of scanf, I suggest reading this scanf manual carefully and answering the following questions:
int x = scanf("%d", &employee[employee_num].salary); What do you suppose x will be if I enter "fubar\n" as input?
Where do you suppose the 'f' from "fubar\n" will go?
If it's ungetc'd back onto stdin, what would your next employee's last name be?
int x = scanf("%d", &employee[employee_num].salary); What do you suppose x will be if I run this code on Windows and press CTRL+Z to send EOF to stdin?
int x = scanf("%d %d", &y, &z); What would you expect x to be, presuming scanf successfully puts values into the two variables y and z?
P.S. EOF can be sent through stdin in Windows by CTRL+Z, and in Linux and friends by CTRL+D, in addition to using pipes and redirection to redirect input from other programs and files.
The problem is that fgets returns the string with the line break (\n) included. So, even the user presses return without entering info, the string won't be empty. Also, your buffer size for salary is too small.
So, either you strip out the \n on every fgets or you change your check to:
if(strlen(employee[employee_num].last_name) == 1) break;
Also, when you're getting the buffer, change 1 to something bigger, like
fgets(strng_buffer, 10, stdin);
However, if you do want to strip out the \n from each fgets, you can do something like:
employee[employee_num].last_name[strlen(employee[employee_num].last_name)-1] = 0;
You can do this for every string or, better yet, create a function that does it.
EDIT: if you can guarantee that the user will press enter after each input then you can safely assume this. However if it's not always the case it's possible that the last character is not \n and just stripping this way might cause problems.

Placement of function to restart loop in C

I've put together a simple currency converter program from code I've edited from the web and a switch statement that I've put in. When I ran the program in Visual Studio:
printf("Would you like to make another conversion? y/n\n");
fflush(stdin);
scanf("%c",&marker);
would wait for input and then either go back to the start of the while statement or exit the console window. Now if I run this is Xcode on a mac "Would you like to make another conversion..." is printed but doesn't wait for an input, it just goes straight back to the while loop.
Am I putting the printf("Would you like...etc section in the right place? Or is there a better way of getting loops to run again after they take input from a user? Do I need to do anything with a 'bool' statement. We've not got that far in class yet.
Full code is shown below:
#include <stdio.h>
int main ()
{
int choice;
float money;
float total;
char marker='y';
printf("\n\nCURRENCY CONVERSION\n");
printf("***Rates correct as of 26 NOV 12***\n");
printf("------------------------------------\n");
printf("1. Australian Dollar (AUD) 1.533=1 GBP\n");
printf("2. Euro (EUR) 1.235=1 GBP\n");
printf("3. Indian Rupee (INR) 89.494=1 GBP\n");
printf("4. Japanese Yen (JPY) 131.473=1 GBP\n");
printf("5. US Dollar (USD) 1.602=1 GBP\n");
printf("Enter the number for the currency to convert...");
while(marker!='n')
{
printf("\n\nWhat would you like to convert your money to? (1-5): ");
scanf("%d",&choice);
printf("\n\nHow much money do you want to convert? (GBP): ");
scanf("%f",&money);
switch(choice) {
case 1:
total = money * 1.533;
printf("\n\nYou will have %.2f Australian Dollars \n\n", total);
break;
case 2:
total = money * 1.235;
printf("\n\nYou will have %.2f Euros \n\n", total);
break;
case 3:
total = money * 89.494;
printf("\n\nYou will have %.2f Indian Rupees \n\n",total);
break;
case 4:
total = money * 131.473;
printf("\n\nYou will have %.2f Japanese Yen \n\n", total);
break;
case 5:
total = money * 1.602;
printf("\n\nYou will have %.2f US Dollars \n\n", total);
break;
default:
printf("You did not choose a correct option\n");
}
printf("Would you like to make another conversion? y/n\n");
fflush(stdin);
scanf("%c",&marker);
}
return 0;
}
Many thanks for any comments of input received.
Your problem is likely that the scanf("%c", &marker); is reading a newline left over by a previous input.
Note that fflush(stdin) invokes undefined behaviour. The C standard says of fflush():
If stream points to an output stream or an update stream in which the most recent operation was not input, fflush() shall cause any unwritten data for that stream to be written to the file.
If that operation clears the input queue in Visual Studio or on Windows, then you've just learned the hard way that extensions to the standard on one platform do not always work on other platforms. In particular, fflush(stdin) does not clear queued characters waiting to be read.
Note that you could have helped your debugging efforts by printing the erroneous input as part of the error message.
You should also be testing each scanf() call:
if (scanf("%c", &marker) != 1)
...EOF or other problem...
if (scanf("%f", money) != 1)
...EOF or maybe a letter instead of a number...
Stylistically, it is best to include a break; after the code after the default: label in the switch.
Also, you should really avoid repeating the conversion rates in the strings that are printed in the menu and in the case statements. Similarly with currency names. Maybe you haven't covered arrays and structures yet, but in the long term, that is a recipe for problems (if only wasted recompilations because the rates have changed; but allowing them to be read from a file, or a web site, is another major exercise).
This isn't anything to do with the loop. It's about your calls to scanf.
What's happening is that stdin is buffered. scanf will only cause the program to halt and wait for input if there isn't enough pending data in the buffer. When you do this:
scanf("%f", &f);
...then the program will halt until you type a line of text. Say you type:
1\n
The 1 is read into the variable f... but the newline remains in the buffer.
This means that your call to:
scanf("%c", &c);
...will return immediately, because there's enough data in the buffer to complete the call. The program will only stop and wait for more data when it runs out of buffer.
Windows doesn't do buffering like this, which is why you weren't observing the behaviour there.
There are about a million solutions to this, ranging from changing the way stdin is buffered to using a text-based UI library like ncurses. Given that by default stdin is line-buffered --- data is read a line at a time, terminated with newlines --- the simplest is simply to make sure that each call to scanf() consumes a complete line of text. That means that the buffer will be empty each time you call scanf. For example:
scanf("%f\n", &f);
...and:
scanf("%c\n", &c);
BE AWARE that if the user enters input that doesn't match this format string, such as 1 2 for the first or ab for the second, scanf will leave the variable uninitialised. So you absolutely have to check the return parameter.
if (scanf("%f\n", &f") != 1)
error("Oh noes!");
Otherwise your program will go horribly, horribly wrong.
As an aside: you haven't done it in this program, but just so you're aware, scanf("%s") should not be used. For anything.
You can do an infinite loop(eg. while(true){} ), and in your switch statement have a case for choice 'n' that breaks the loop (eg. break;)
Also, case 1 , case 2, etc... I would scan them as chars instead of integers. (eg. case '1':, case '2':, etc...)
I am not sure what I just said is going to work but that is my initial thought. Hope it helps :)
If you don't want to deal with flushing buffers, you can define a char buffer[1024] and use fgets with sscanf:
printf("\n\nWhat would you like to convert your money to? (1-5): ");
fgets(buffer, sizeof(buffer), stdin);
sscanf(buffer, "%d", &choice);
and the while loop can include your menu:
while (1) {
printf("\n\nCURRENCY CONVERSION\n");
printf("***Rates correct as of 26 NOV 12***\n");
/* ... */
printf("Would you like to make another conversion? y/n\n");
fgets(buffer, sizeof(buffer), stdin);
sscanf(buffer, "%c", &marker);
if (marker == '\n')
break;
}

why does scanf not wait for user input after it fails one time?

I face problem in my code below. When I enter any integer, that is (0-9), then it is OK. But when I enter some other characters like A, B or any other letter scanf() fails. Then it doesn't wait for any further input.
I've attached a code snippet for this. I highly require error handling using only the C standard library.
#include <stdio.h>
int main()
{
int choice;
while(1)
{
printf("Enter your choice: ");
if(0 == scanf("%d", &choice))
{
printf("Some error occured\n");
//break;/*I did not want to break this loop as I use this to show menu*/
}
switch(choice)
{
case 0:printf("Case 0\n");break;
case 1:printf("Case 1\n");break;
case 2:printf("Case 2\n");break;
case 3:printf("Case 3\n");break;
case 4:printf("Case 4\n");break;
case 5:printf("Case 5\n");break;
case 6:printf("Case 6\n");break;
case 7:printf("Case 7\n");break;
case 8:printf("Case 8\n");break;
case 9:printf("Case 9\n");break;
default:printf("Default case\n");break;
}
}
}
Question more clearly is: why doesn't it wait after failure?
With scanf, if the input given doesn't match the format specification, then the input isn't consumed and remains in the input buffer. In other words, the character that doesn't match is never read.
So when you type, e.g. an a character, your code will loop indefinitely as scanf continues to fail on the same character.
When scanf returns an error, you need to get rid of the offending character(s).
You can't know how many characters will be typed, but one simple way to consume any offending characters is to try and read a string when scanf fails:
char junkChars[256];
printf("Enter your choice: ");
if (scanf("%d", &choice) == 0)
{
printf("Some error occured\n");
scanf("%s", junkChars);
}
This will clear the input buffer, but relies on an upper-bound. If the user types more than 256 characters, the scanf will continue to consume them on the next iteration(s).
Clearly this is an ugly solution, but scanf isn't really a great input mechanism for this reason. Better to use fgets to get the input line as a string, then control the parsing of that string yourself.
That is by design.
Don't use break, use continue.

why i'm having problems with scanf and gets?

when i'm trying to use scanf and gets, i'm having trouble, i need to put gets twice, if i put it once my program is terminated, it's homework and i have to use those functions in those places.
the code :
int main()
{
int i=0,operation=0;
char str[100];
printMenu();
scanf("%d",&operation);
switch (operation)
{
case 1:
printf("Please, enter your sentence >");
gets(str);
gets(str);
if (checkChars(str))
inputSent(str);
else
printf("ERROR: Incorrect data, try again.");
break;
}
return 0;
}
So, we have a stdin stream of data. This is the input that you write at the terminal.
When you call scanf, it only reads the decimal number that you wrote. It does not read the newline after it. Therefore, you need two calls to gets because the first call only sees '\n' while the second sees your actual data.
To fix this, I'd recommend that you use gets to read the full line when you get the operation, and then use sscanf to get the operation.
For example:
printMenu();
gets(str)
sscanf(str, "%d", &operation);
switch (operation) {
What's happening is after your first scanf() is that there's still data lingering in the input buffer (any form of whitespace that delimits things, usually a return) and when you call the next gets() it returns immediately because it read the "enter" you hit after typing in a number.
In order to solve this, you'll need to flush the input buffer. This is good practice especially when switching between reading individual character words and sentences. Here's a small snippet I found from another thread that may help you (this would go after your scanf()):
while (getchar() != EOF);
What it does is continually read characters out of the input buffer until none are left. This may not be the best solutions for all situations but it should be enough to help you through this assignment.

Resources