The following is the code for a simple menu. If the user enters an invalid option (4 or 'o') the menu should be reprinted displaying an error message. The code works when the user inputs a number, however it fails (looping indefinitely) when the user inputs a letter or string into option. My guess is that memory is being overwritten in the looping cases generating the strange behavior. Could someone help me to fix it?
Thank You
int inmenu = 1;
while (inmenu){
//Menu
printf("User: %s %s\n", user.firstname, user.lastname);
printf("0) Exit\n1) List Friends\n2) Add Friend\n3) Delete Friend\nWhat would you like to do? ");
int option;
scanf("%i", &option);
if(option == 0)
inmenu = 0;
else if (option == 1)
defaultPrint(friends, numfriends, NULL);
else if (option == 2){
//addfriend
char *name = (char *) malloc(sizeof(char) * 256);
int birthdate;
printf("Enter first name: ");
scanf("%s", name);
printf("Enter last name: ");
scanf("%s", name);
printf("Enter birthdate: ");
scanf("%i", &birthdate);
}
else if (option == 3){
//deletefriend
defaultPrint(friends, numfriends, NULL);
int n;
printf("What friend would you like to delete? ");
scanf("%i", &n);
}
else
printf("ERROR: Invalid option %i\n", option);
}
Test input:
0) Exit
1) List Friends
2) Add Friend
3) Delete Friend
What would you like to do? 4
ERROR: Invalid option 4
0) Exit
1) List Friends
2) Add Friend
3) Delete Friend
What would you like to do?
(correct behavior)
0) Exit
1) List Friends
2) Add Friend
3) Delete Friend
What would you like to do? o
What would you like to do? ERROR: Invalid option 4
0) Exit
1) List Friends
2) Add Friend
3) Delete Friend
...
(incorrect behavior keeps printing the same last 5 lines in an infinite loop)
Why use scanf? How about use getchar, check the range, see if it's 0-4, and if not, error message.
The cause of your problem is that scanf() doesn't consume the input stream if it doesn't match the requested format.
This means that when you call:
scanf("%i", &option);
If the user enters something other than a number (like "o"), then that user input remains in the input stream - so when you loop around and call scanf() again, the "o" is still there, and still doesn't match the requested format.
This is why scanf() is not particularly suitable for taking user input - it's designed for consuming well-formatted data files. Instead, since you have a line-oriented user interface, you should read an entire line from the user with fgets(), then try parsing it with sscanf(). If it doesn't parse correctly, you can report an error and then read a new line from the user.
When you enter a number and press enter, scanf reads the number but leaves the newline in the buffer, when the loop loops, scanf will read the newline from the buffer, see that it doesn't match the format, and leave it there. This will repeat forever.
A quick fix could be to add the newline to the format:
scanf("%i\n", &number);
but this will fail if the user doesn't enter exactly the data you expect, newline isn't the only thing that will cause the behaviour, anything that doesn't match the format is left in the buffer.
Related
I have a project that must be made only in C (I'm not allowed to use C++).
Right now I'm working on the UI (a little menu showed in the console, each option has a number from 0 to 6 assigned to it, the user typed the number, hits enter and so on).
I'm having a problem with the reading option function.
At first I tried
int option;
scanf("%d", &option);
but this caused problems when I typed in characters.
I tried reading a char:
char option;
scanf("%s", &option);
option -= '0';
This allowed me to treat it like a number and worked nice for the first tests, allowing me to verify if the option is valid (it's not a letter, it's a number between 0 and 6).
The problem is that I can type more than one character and all of them will be stored somewhere in memory. And that's obviously bad.
I tried reading with "%c", but that will display the error message for every character in the string I entered.
To make it a bit more clear, this is the function
int readOption(int maxOp)
{
char option = -1;
while(option < 0 || option > maxOp)
{
scanf("%c", &option);
option -= '0';
if(option < 0 || option > maxOp)
printf("Invalid option!\nTry again: \n");
}
return option;
}
If I type "abc", the error message will be printed 3 times.
So how can I make sure that any extra characters entered are ignored?
Try this
int ch;
scanf(" %c", &option);
while((ch = getchar()) != '\n');
Untested, buy you may try something like this:
scanf(" %[0-6]1d*[^\n]", &option);
The call to scanf will only be valid if the input is a single number between 0 and 6, ignoring leading spaces. * suppresses any character after that isn't in this range and that is not a newline.
You can use getc() to do the job like this:
int main(){
char choice = getc(stdin);
fflush(stdin);
}
Now choice is the first entered character, everything entered after the first character is deleted so it won't interrupt any other user inputs.
In general, when the user enters input it's stored in a special buffer called stdin, if you don't delete that buffer after reading the first character using fflush(stdin) it will interrupt any future user inputs.
So I am trying to write a program that will let me read a user input for data on an MP3 file using a doubly linked list data structure. I got most of the methods and functions to work, but when I am prompting the user to put in input it prints out two lines before the user can input for the first line. So for example
int main()
{
int user_input = 0;
while(!(user_input >= 4))
{
struct MP3_data_node* MP3_data;
MP3_data = (struct MP3_data_node*)malloc(sizeof(struct MP3_data_node));
printf("\nPlease select a number for one of the following instructions:\n");
printf("0: add to list\n1: delete from list\n2: print the list from beginning to end\n3: print the list from end to beginning\n4: exit\n");
scanf("%d", &user_input);
if(user_input == 0)
{
printf("Please provide the artist:");
fgets(MP3_data->artist,50,stdin);
printf("Please provide the album:");
fgets(MP3_data->artist,50,stdin);
printf("Please provide the song title:");
fgets(MP3_data->artist,50,stdin);
printf("Please provide the year the song was released: ");
scanf("%d", &MP3_data->yearReleased);
printf("Please provide the length of the song in seconds: ");
scanf("%d", &MP3_data->runTime);
addToList(MP3_data);
}
...
So it prints out "Please provide the artist:Please provide the album:" and then let's me put the input in, so my question is how do I make it so that it prints:
Please provide the artist: (user input)
Please provide the album: (user input)
etc.
You're doing the right thing (fgets) int the first few prompts, then you switch to scanf which is the source of your problem. Use fgets (and strtol) instead of scanf and you will be fine. (And, the first scanf which causes the problem described in your question.)
The problem is that scanf only reads the digit part of whatever you enter. That means if you type 12Enter, then the scanf reads the 1 and 2 but leaves the Enter in the input buffer for the next call to fgets or scanf. On the other hand, fgets reads everything you type including the Enter, avoiding this problem.
This function will be called by the menu.
void exponentiation()
{
int i, result = 0, first, second;
printf("\n%s\n%s\n\n%s",
"1) Exponentiation",
"------------------",
"Enter 1st integer: ");
scanf("%d", &first);
printf("Enter 2nd integer: ");
scanf("%d", &second);
printf("%d raised to %d equals %d\n", first, second, result);
main();
}
From this function I need to read the user input, if the user input is "enter" without any integer, it should be going back to the menu which is calling the main().
I already tried to get the input.
For example:
if(first == '\n')
{main();}
or
if(first == 10) /**which is 10 is ASCII code for enter**/
{main()}
Both ways it didn't work at all, any suggestions?
both ways it didn't work at all, any suggestion
The function scanf returns the number of items it successfully scanned. You should check its return and go back if it doesn't matches your expectations.
Also you should know %d ignores whitespace. So if the user hits return without entering an integer, scanf simply skips over it and waits for something else.
If you insist on not ignoring whitespace this way, you should avoid scanf and use other input methods such as fgets. Get input from the user line by line and use sscanf, strtoul and strtok to make sense of it.
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.
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.