C - user single char input rendering weird results - c

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.

Related

Scanf skips functions

I am working on my assignment and this is the issue that I bumped into. In the assignment, it says that the input value for the middle initals should be this - "L. A.". However, once I run my program it prints some printf functions on the same line, skipping the scanf function. I have went through a lot of topics about that " %c" issue, but I still can not make my program run properly. Some of the variables are from .h file. The actual assignment is bigger, however it is pretty much repetative so I thought if I figure out how to fix this certain issue I will be able to finally finish my assignment.
int main(void){
// Declare variables here:
char ch;
struct Name FullName = { {'\0'} };
struct Address AddressInfo = { 0, '\0', 0, '\0', '\0' };
struct Numbers PhoneInfo = { {'\0'} };
// Display the title
printf("Contact Management System\n");
printf("-------------------------\n");
// Contact Name Input:
printf("Please enter the contact’s first name: ");
scanf("%s", &FullName.firstName);
printf("Do you want to enter a middle initial(s)? (y or n): ");
scanf(" %c", &ch);
if (ch == 'y') {
printf("Please enter the contact’s middle initial(s): ");
scanf(" %s", FullName.middleInitial);
}
printf("Please enter the contact’s last name: ");
scanf(" %s", &FullName.lastName);
// Contact Address Input:
printf("Please enter the contact’s street number: ");
scanf("%d", &AddressInfo.streetNumber);
OUTPUT (I have highlighted input values):
Contact Management System
-------------------------
Please enter the contactÆs first name: *Artem*
Do you want to enter a middle initial(s)? (y or n): *y*
Please enter the contactÆs middle initial(s): *L. A.*
Please enter the contactÆs last name: Please enter the contactÆs street number:
The %s format specifier reads a sequence of characters terminated by whitespace. When you enter L. A., only L. gets read into middleInitial because it stops reading at the space and A. is left in the input buffer. On the next scanf, it immediately reads those buffered characters so it doesn't stop to prompt for anything.
The simplest way to handle this is to leave out the space when inputting, i.e. L.A.. If you want to support whitespace, you'll want to get rid of scanf entirely and read everything a full line at a time using fgets. Note that fgets also reads in the trailing newline, so you'll need to strip that out.

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

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`

Scanning code doesn't work

Okay so I am doing an assignment for a class in which I have to read the 4 colour bands on a resistor and determine its resistance value. In order to do this I wrote code first asking the user to input the letter which corresponded to a specific band colour. For example R corresponds to red. Here is the code I wrote:
char band1, band2, band3, band4;
float firstband, secondband, thirdband, fourthband;
float value1, value2, value3, value4, finvalue;
printf("Please enter the 1st band: ");
scanf("%c", &band1);
printf("Please enter the 2nd band: ");
scanf("%c", &band2);
printf("Please enter the 3rd band: ");
scanf("%c", &band3);
printf("Please enter the 4th band: ");
scanf("%c", &band4);
The problem is that the when the user is asked to input the letters the request gets jumbled up. It gives me this:
Please enter first band:
Please enter the second band: Please enter the third band:
Please enter the fourth band: Resistance is ...
Only the first band value and the third band value are read, the second and fourth are skipped. I can't seem to find the error in the code.
You are feeding scanf references to single char variables. You seem to want to input strings, which are arrays of char.
Looks like this:
char aString[20];
printf("Enter a string, no more than 19 characters: ");
scanf("%s", aString);
It says "no more than 19 characters" because strings in C are null-terminated.
This is however just written in an easy way to understand. It's error-prone though. As pointed out in the comments, this would be more robust:
if (1 != scanf("%19s", aString))
{
Handle_problem();
}
You didn't do any error checking or input clearing. As a result, the next scanf call "eats up" the invalid characters from the previous one resulting in the call seeming to be "skipped".
In order to fix this, you can use the following function to clear the input buffer (which is what scanf reads from first):
void clear_input() {
int ch = 0;
while(( ch = getchar) != '\n' && ch != '\0' && ch != EOF );
}
Also, it is a good idea to get into the habit of checking scanf's return value; this ensures valid input is given.:
int check = -1;
do {
check = scanf( "%c", &variable );
clear_input();
} while (check != 1);
An alternative to scanf if you only need a single character might be to use getchar( void ). getchar() obtains a single character from the user and returns the value of that character. Even if you take this route, you still have to clear the input buffer.

How can I read only one character and ignore the others if the users types more?

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.

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.

Resources