Ignoring scanf value in while loop - c

I'm learning C and I face a problem running this loop. I've wrote a while loop to prompt the user to key in the package and the quantity of it. I try to validate the input for the quantity to check is it integer or not (when a user key in a character it will prompt the user to key in again)
For the first run, everything is fine.
But when the loop runs a second time and so on, I try to key in a character for the quantity of the package, the message won't pop up to tell the user to key in again.
The value of the scanf is ignored and the value of tempQtty is equal to the previous quantity that the user keyed in.
Is there any way to fix this, or is there another way to validate the user input is integer?
Sorry for my broken English :")[input, expected input and actual input][1]
while(skip != 'x')
{
printf("\n\n%27sPACKAGE A/B/C/D ( x = skip ) : ", "");
rewind(stdin);
package = getchar();
switch (package)
{
case'x':case'X': skip = tolower(package); break;
case'A':case'a':case'B':case'b': case'C':case'c':case'D':case'd':
printf("%27sQUANTITY%21s: ", "", "");
rewind(stdin);
scanf("%d", &tempQtty); //here's the problem
while (tempQtty < 0)
{
printf("%27s(PLEASE KEY IN A CORRECT VALUE!)\n", "");
printf("%27sQUANTITY%21s: ", "", "");
rewind(stdin);
scanf("%d", &tempQtty);
}
switch (package)
{
case 'A':case 'a': qttyA = tempQtty; totalQttyA += tempQtty; break;
case 'B':case 'b': qttyB = tempQtty; totalQttyB += tempQtty; break;
case 'C':case 'c': qttyC = tempQtty; totalQttyC += tempQtty; break;
case 'D':case 'd': qttyD = tempQtty; totalQttyD += tempQtty; break;
}
break;
default:
printf("%27s(NO SUCH PACKAGE! PLEASE KEY IN AGAIN!)\n", "");
}
}
printf("\nA = %d", qttyA);
printf("\nB = %d", qttyB);
[1]: https://i.stack.imgur.com/hBD82.png

First of all, and as I mentioned in a comment, this is a surprisingly complicated problem, and you are not alone in facing it. It's not a problem with you, or with the C language; it's basically just a problem with the scanf function itself.
Partial answer:
(1) Everywhere you have things like
scanf("%d", &tempQtty);
while (tempQtty < 0)
...
that's wrong. If you ask for integer input using %d, and if the user types something non-numeric, what scanf does not do is fill in tempQtty as -1. What it does do is return a value saying it couldn't convert what you (the programmer) asked. So you want to change this to something more like
while (scanf("%d", &tempQtty) != 1)
...
(2) If the user does not type the integer you requested, and if scanf returns 0 to tell you so, there's a problem: the non-numeric input the user typed is probably still sitting on the input stream. It looks like you may have realized this, and that you're trying to get rid of the unread input by calling rewind(stdin). But that won't work; that's not the way to do it.
What you want to do is write a little "helper" function like this:
void flush_unread_input()
{
int c;
do {
c = getchar();
} while(c != EOF && c != '\n');
}
Then, wherever you've detected an error (that is, wherever scanf has returned something other than 1), instead of calling rewind(stdin), just call flush_unread_input().
A few more points:
You may have to experiment where to call flush_unread_input and where not to. You can't just blindly sprinkle it everywhere, because it basically reads and discards the rest of the line, but that it means it can also read and discard an entire line, which might sometimes be a line that you actually wanted.
There are many ways to write a flush_unread_input function. I've shown one way that should be easy to understand, but you'll often see something more compact like while((c = getchar()) != EOF && c != '\n');. (Also I haven't tested the version I've shown here.)
My answer might have suggested that scanf returns 1 if it succeeds and 0 if it fails, but it's more complicated than that. scanf actually returns the number of items successfully converted and stored, which might be more than 1 if you have a format specifier with multiple % signs in it.
There's much more to say on this topic, but I don't have time to write a longer answer this morning.

I try to key in a character for the quantity of the package, the message won't pop up to tell the user to key in again.
The value of the scanf is ignored and the value of tempQtty is equal to the previous quantity that the user keyed in.
Is there any way to fix this, or is there another way to validate the user input is integer?
There is no base for your assumption that tempQtty would receive a negative value if you entered z. In fact, the C standard mandates that tempQtty is not affected if the conversion fails. The way to fix this is to not ignore the scanf return value, which tells whether input was valid.

Related

Check if user input is between 1 and 10

part of the program that i am working on is to get a number from the user , but the condition is that it has to be any number between 1 and 10 nothing else, so how could i force the user to only input one of these specific numbers , so that if he wrote any other number or a character , an error message to pop out and repeat the process till he choses correctly
here is the code
// getting a number from the user
int number;
printf("Please pick a number from 1 to 10 : ");
scanf("%d",&number);
printf("Oh ! You have chosen %d\n", number);
// what should i do here ?
C allows for very nice input control, but it is not free... Said differently you cannot rely on the language nor on the standard library but have to code everything by hand.
What you want:
control that the input is numeric
control that the number lies between 1 and 10
How to:
control the return value of the input function
if you got a number control its value
if you got an incorrect input (optionaly) give a message and loop asking
Possible code:
int number;
for (;;) { // C idiomatic infinite loop
printf("Please pick a number from 1 to 10 : ");
if (scanf("%d", &number) != 1) {
// non numeric input: clear up to end of line
int c; // c must be int to compare to EOF...
while ((c = fgetc(stdin)) != EOF && (c != '\n'));
}
else if (number > 0 && number <= 10) break; // correct input: exit loop
// error message
printf("last input was incorrect\n");
}
printf("Oh ! You have chosen %d\n", number);
If you want a more user friendly way, you could use different messages for non numeric input and incorrect values, but it is left as an exercise for you... (I am afraid I am too lazy ;-) )
What you likely envision is a fine-grained control over the character-by-character input of the user; for example, anything but digits should be impossible; or when they type a 2 and try to type another digit, that should be impossible, too.
That's something we know from graphic user interfaces. It requires that your program is "informed" about every key stroke at once.
For historical reasons, this capability is not part of the C standard library. The reason is that historically all kinds of input devices were used, for example punch cards or paper-based teletypes. The communication was line by line: Input was local until the user hit the aptly named "enter" key. Any stupid device can do that, a lowest common denominator which is why all languages which do not define GUI elements adhere to it.
Obviously, character-by-character input is entirely possible on modern terminals and computers; but it is system specific and has never been standardized in the language. It is also likely more complicated than meets the eye if you want to give the user the opportunity to edit their input, a phase during which it may be "illegal". In the end you'll need to catch the point when they submit the entire value and validate it, which is something you can do even with the crude facilities that C provides.
Hints for an implementation:
Let the user complete a line of input. Validate it, and if the validation fails, prompt for another attempt. Do that in a loop until the input is valid.
Use scanf because it is convenient and error free (compared to home-grown input parsing).
This is something often overlooked by beginners: Check the return value of scanf which will indicate whether the input could be parsed (read the scanf manual!).
int main()
{
int number = 0;
while (number < 1 || number > 10)
{
printf("please enter number between 1 - 10\n");
scanf("%d", &number);
if (number < 1 || number > 10)
{
printf("you entered invalid number!\n");
}
}
return 0;
}
while(1)
{
//input ....
if(number<0 || number>10)
{
// print error
continue;
} else {
while (getchar() != '\n') ;
break;
}
}
I think I made a mistake at the beginning.A character can be checked by if but scanf can't. If scanf can't get the input in the specified format, the illegal input in the input buffer will be kept all the time.
After looking at another question, I thought that when the input is wrong, we should use getchar() to clear the buffer before the next input.

How can I make sure the input suits my data types?

I'm new to c language and trying to write a fail-safe in my program in case of wrong input by the user.
This is the fail-safe part:
int h1, m1;
char junk; /* Storing a colon or a period when people type in the hour */
printf("Enter start hour: ");
while (scanf("%d %c %d", &h1, &junk, &m1) != 3)
{
printf("Invalid, try again\n");
}
In any case of wrong input (for example entering a char instead of an int) the loop just continues ad infinitum. Doesn't matter if I scan every variable individually, or assign them 0 at the end of the iteration.
I searched Stack for answers, but only found methods for strings (basically moving to 'fgets' or 'isdigit()') or simply for scanning a single integer.
Is there a way to fail-safe the scan both for int and char data types?
Put simply, no. scanf is not particularly sophisticated and doesn't handle situations where "the user might input an element, but maybe not" very well.
My suggestion is - read the data using fgets or similar, validate it, then translate it once you're confident it's good.
You can use assert.h library to make sure input is valid, as an example you can assert below code snippet to check if h1 is entered in valid hour range.
assert( h1 <= 24 && h1 >= 0);
edit: (thanks to first two comments.)
You can add do junk = getchar(); while (junk != EOF && junk != '\n'); to clear your input buffer from '\n' that cause that endless behavior.

Infinite loop with 'char' type input

I am learning binary search tree. Given below is the main function of a program to perform basic BST operations. The option variable chooses which operation to perform for switch
int main()
{
struct node* tree=NULL;
struct node* ptr=NULL;
struct node* ptrm=NULL;
int val;
int option;
do
{
printf("\n1.Insert Node\n2.Preorder Traversal\n3.Postorder Traversal\n4.Inorder Traversal\n5.find_smallest_element\n6.find_largest_element\n7.Delete Element\n8.Total_nodes\n9.total_external_nodes\n10.total_internal_nodes\n11.Mirror image\n12.Exit\n");
printf("\nEnter option");
scanf("%d",&option);
switch(option)
{
case 1:
printf("\nEnter value to be inserted");
scanf("%d",&val);
tree=insert_element(&tree,val);
printf("\n%d Inserted\n",val);
break;
case 2:
preorder(&tree);
break;
case 3:
postorder(&tree);
break;
case 4:
inorder(&tree);
break;
case 5:
ptr=find_smallest_element(&tree);
printf("\nSmallest element:%d",ptr->data);
break;
case 6:
ptr=find_largest_element(&tree);
printf("\nLargest element:%d",ptr->data);
break;
case 7:
printf("\nEnter value of element to be deleted");
scanf("%d",&val);
tree=delete_node(&tree,val);
break;
case 8:
printf("\nTotal nodes%d",total_nodes(&tree));
break;
case 9:
printf("\nTotal External nodes%d",total_external_nodes(&tree));
break;
case 10:
printf("\nTotal Internal nodes%d",total_internal_nodes(&tree));
break;
case 11:
ptrm=mirror_image(&tree);
}
}while(option!=12);
return 0;
Everything works fine when i give int data as input for 'option'.However, when i give a char input the program goes into infinite loop and displays option list repeatedly.
Why does this happen?
Since you used %d format specifier in the scanf() format string,
scanf("%d",&val);
will successfully assign to val only if an integer was given as the input. If a char is given instead, scanf() (which returns the number of successful assignments) will return 0 here and will leave the char in the input buffer unconsumed.
During the next iteration of the loop, this char would still be in the input buffer and scanf() would end up trying to read the same thing and will won't assign to val once again.
This will go on and on resulting in an infinite loop.
To solve this, check the value returned by scanf(). If it is not 1, clear the input buffer till the next \n (newline) like
int t;
while( (t=getchar()) != `\n` );
This will consume the old data till a \n from the input buffer.
You could then use the continue statement to skip the rest of that iteration of the loop.
Read about getchar() here.
Why does this happen?
The roots of this issue stem all the way back to how scanf indicates error codes to your code (not at all, because your code discards them), and what scanf("%d", &val) is expected to do when non-decimal input is encountered; it stops reading input, possibly returning an error code, but your code discards that and continues on merrily trying to delete the node indicated by the value which may not have been read, leading to possible use of an uninitialised variable later...
Some people take the guessing to an extreme, and think it's appropriate to use fflush(stdin) to solve this (it isn't; don't do that...). You've not gone that far, but I think it might be a good idea to start reading the manuals of the functions you're using. The scanf manual is here. Make note of that URL, and realise that you can look up other standard functions (both C99 and POSIX standard) by substituting the name of the function.
The first thing your code must do is check that return value, which your manual will document in the RETURN VALUES section; as with most standard library functions, scanf has a return value which your code should most likely contain critical logic regarding! From there, how you handle errors is your business. Perhaps it might be appropriate to use something simple yet user-unfriendly, like:
perror(scanf);
exit(EXIT_FAILURE);
You should seek the simpler solutions where possible, to avoid overcomplicating things. If your input doesn't come directly from the user, or you just want to prototype, you should use the solution above. You can always change exit(EXIT_FAILURE) to return EXIT_FAILURE; and return 0; on success, if necessary later.
If you choose to keep your program running, how much of the user input gets discarded due to the typo is up to you. By far the simplest option is to just read a single character (using getchar();)...
You could choose to discard a word of input, like so: scanf("%*s");. The * informs scanf to read and discard the input, rather than reading and assigning.
Neither of those options strike me as being particularly user-friendly. If you're going to the effort of making a user-friendly interface, you'll probably want to choose one of the following options.
Using the * assignment-suppression modifier, you can also discard a line of input, like so:
scanf("%*[^\n]");
getchar();
The getchar(); is necessary to discard the newline character, which we expect to be discarded when a line is discarded.
Using the command line arguments for your input, rather than using stdin (or other files/streams). Some surprisingly simple yet versatile menus have been produced this way, such as the ones your compiler presents to you. Your mode of input then changes to using friendlier functions such as sscanf, and developing your program not as a looping program that remains open, but as an instant program which gets executed every now and then, when necessary, to update records or what-not.
Using the graphical user interface instead of the console. Well, that one really makes the ol' noggin' flog, eh? You could use... a context menu such as the File/Edit/etc menus in Windows, or a listbox (which would be more touch-screen friendly) to prompt your user for a selection.
Suffice to say, this looks like homework, so you probably don't have the choice to design a more appropriate user interface... In this case, I suggest using the * assignment-suppression modifier as per above (the first bolded section).

Function to test the type of the entered value

I want to create a loop that will keep reading values (Integers in here) until a non-integer is entered.
For example, here is the code I've written
while(true)
{
scanf("%d",&w);
if (w==100) break;
else fwrite(&w,sizeof(int),1,f);
}
Only right there the test is for the user to type in the number 100. But what I'm of searching for is a function or some kinda way that will test whether it's an integer or not or one that will detect an error with the entered value. isdigit() doesn't seem to pull the trick over here.
Any ideas?
You can check the return value form scanf
if (scanf("%d", &w)) != 1) {
/* error, input doesn't match. */
}
Alternatively you can ditch scanf and just read a string with fgets. Then you can walk the string and do isdigit and decide if you like the input or not.

Do while loop not exiting despite expression becoming false

I've got a program here which contains a do-while loop within a specified void method. I'm trying to exit the loop within the function, so that the do-while loop actually works as it is supposed to. Except after I run the program and one of the cases occurs, the program continues to run despite my while statement stating that it should only work while(userInput != 1).
I cannot use global variables to solve this problem, as my assignment limits me on using such techniques, thus any help would be much appreciated!
Here is a snippet of my code:
void functionTest()
{
int gameOver = 0;
int userInput;
do
{
printf("please enter a number 1-3");
scanf("%d",&userInput);
switch(userInput)
{
case 1:
printf("You entered %d",userInput);
gameOver = 1;
break;
case 2:
printf("You entered %d",userInput);
gameOver = 1;
break;
case 3:
printf("You entered %d",userInput);
gameOver = 1;
break;
}
}
while(gameOver!= 1);
}
}
The problem probably lies when you use scanf(). Something that you're inputting before hitting enter is not 1, 2 or 3. Could you tell us exactly what you type when it asks you to enter a choice?
Sometimes, the standard output needs to be flushed before using a fresh scanf(). Try fflush(stdout) before the scanf line.
See older question 1 and older question 2.
EDIT:
I can reproduce the problem easily enough if I enter anything apart from "1","2" or "3"...
I would suggest, you do the following before executing the switch statement:
Add fflush(stdout) before scanf()
Accept the input as a string (%s) instead of a number. (char [] needed)
Trim the string of trailing and leading white spaces.
Convert to number using a library function
Then switch-case based on that number
The problem is that if other characters (that aren't part of an integer) are present in the input stream before an integer can be read, scanf() fails and unusable data is never cleared out... which leads to an infinite loop (where scanf() repeatedly fails to read the same characters as an integer, over and over).
So you need to read off the invalid characters when scanf() fails, or as part of the format.
A simple fix would be to change your scanf from:
scanf("%d",&userInput);
to:
scanf("%*[^0-9]%d",&userInput);
to read (and discard) any characters in the input stream that aren't digits 0-9 before reading your integer... but that still doesn't check whether scanf fails for any other reason (like a closed input stream).
You could replace it with something like this:
int scanfRes,c;
do {
scanfRes = scanf("%d",&userInput); /* try to read userInput */
/* ..then discard remainder of line */
do {
if ((c = fgetc(stdin)) == EOF)
return; /* ..return on error or EOF */
} while (c != '\n');
} while (scanfRes != 1); /* ..retry until userInput is assigned */
..which will retry scanf() until the field is assigned, discarding the remainder of the line after each attempt, and exiting the function if fgetc() encounters an error or EOF when doing so.

Resources