How do I fix scanf? (FailSafes) [duplicate] - c

This question already has answers here:
Why does scanf appear to skip input?
(7 answers)
Closed 8 years ago.
Here i have a simplified piece of code that asks and displays a number on a loop, it works fine for all numbers i type in, But if i input a letter or a special character (!"£$%^&*-_=+ etc ) it goes mental and skips the input.
#include<stdio.h>
int number;
int main()
{
do
{
system("cls");
printf("Enter a number");
scanf("%d",&number);
}
while(1==1);
}
My question is, what can i do to stop this from happening?, is there some code that filters out this nonsense or is scanf pretty much worthless?
//Edit: This is somehow been marked as a duplicate, heh.

From here:
if the input doesn't conform to the expected format scanf() can be
impossible to recover sensibly [..] A "better" alternative here is to
use an input function like fgets() or fgetc() to read chunks of input,
then scan it with sscanf() or parse it with string handling functions
like strchr() and strtol().

scanf with %d will fail to scan an integer and returns 0 when a character was entered. So just check if it doesn't return 1. If it doesn't , a character was entered (if it returned 0) or else, an integer was entered. Note that if EOF was encountered, scanf will return -1.
if(scanf("%d", &number) != 1)//character entered
{
printf("Invalid input\n");
scanf("%*s");//clear the invalid character(s) from stdin
}
else
{
//a number was entered
}
The reason that scanf becomes "mental" and the program prints Enter a number many times when you enter a character is that when the scanf fails to scan an integer from the standard input stream(stdin), it returns 0 and the execution continues. When scanf is called the next time, it sees the characters which you had entered the last time and again fails and this process continues. To prevent it, just clear the stdin like I've done in the code above.
Another popular way of clearing the stdin is using:
int c;
while((c = getchar()) != '\n' && c != EOF);

Related

C Programing : scanf statement is not working in goto [duplicate]

This question already has answers here:
scanf() leaves the newline character in the buffer
(7 answers)
Closed 6 years ago.
I have this block of code (functions omitted as the logic is part of a homework assignment):
#include <stdio.h>
int main()
{
char c = 'q';
int size;
printf("\nShape (l/s/t):");
scanf("%c",&c);
printf("Length:");
scanf("%d",&size);
while(c!='q')
{
switch(c)
{
case 'l': line(size); break;
case 's': square(size); break;
case 't': triangle(size); break;
}
printf("\nShape (l/s/t):");
scanf("%c",&c);
printf("\nLength:");
scanf("%d",&size);
}
return 0;
}
The first two Scanf's work great, no problem once we get into the while loop, I have a problem where, when you are supposed to be prompted to enter a new shape char, it instead jumps down to the printf of Length and waits to take input from there for a char, then later a decimal on the next iteration of the loop.
Preloop iteration:
Scanf: Shape. Works Great
Scanf: Length. No Problem
Loop 1.
Scanf: Shape. Skips over this
Scanf: length. Problem, this scanf maps to the shape char.
Loop 2
Scanf: Shape. Skips over this
Scanf: length. Problem, this scanf maps to the size int now.
Why is it doing this?
scanf("%c") reads the newline character from the ENTER key.
When you type let's say 15, you type a 1, a 5 and then press the ENTER key. So there are now three characters in the input buffer. scanf("%d") reads the 1 and the 5, interpreting them as the number 15, but the newline character is still in the input buffer. The scanf("%c") will immediately read this newline character, and the program will then go on to the next scanf("%d"), and wait for you to enter a number.
The usual advice is to read entire lines of input with fgets, and interpret the content of each line in a separate step. A simpler solution to your immediate problem is to add a getchar() after each scanf("%d").
The basic problem is that scanf() leaves the newline after the number in the buffer, and then reads it with %c on the next pass. Actually, this is a good demonstration of why I don't use scanf(); I use a line reader (fgets(), for example) and sscanf(). It is easier to control.
You can probably rescue it by using " %c" instead of "%c" for the format string. The blank causes scanf() to skip white space (including newlines) before reading the character.
But it will be easier in the long run to give up scanf() and fscanf() and use fgets() or equivalent plus sscanf(). All else apart, error reporting is much easier when you have the whole string to work with, not the driblets left behind by scanf() after it fails.
You should also, always, check that you get a value converted from scanf(). Input fails — routinely and horribly. Don't let it wreck your program because you didn't check.
Try adding a space in the scanf.
scanf(" %d", &var);
// ^
// there
This will cause scanf() to discard all whitespace before matching an integer.
Use the function
void seek_to_next_line( void )
{
int c;
while( (c = fgetc( stdin )) != EOF && c != '\n' );
}
to clear out your input buffer.
The '\n' character is still left on the input stream after the first call to scanf is completed, so the second call to scanf() reads it in. Use getchar().
When you type the shape and ENTER, the shape is consumed by the first scanf, but the ENTER is not! The second scanf expects a number so, the ENTER is skipped because is considered a white space, and the scanf waits for a valid input ( a number) that, again, is terminated by the ENTER. Well, the number is consumed, but the ENTER is not, so the first scanf inside the while uses it and your shape prompt is skipped... this process repeats. You have to add another %c in the scanfs to deal with the ENTER key. I hope this helps!
You can also use
scanf("%c%*c", &c);
to read two characters and ignore the last one (in this case '\n')

Can somebody explain why the line of this code containing fgets isn't getting executed? [duplicate]

This question already has answers here:
fgets doesn't work after scanf [duplicate]
(7 answers)
Closed 1 year ago.
#include <stdio.h>
#include <string.h>
int main()
{
int num;
char answer[10];
char affirmation[10]="yes";
do
{
printf("Enter a number : \n");
scanf("%d",&num);
if (num % 97 == 0)
{
printf("No. is divisible by 97!\n");
}
else
{
printf("No. is not divisible by 97!\n");
}
printf("Once More ? [yes/no] \n");
fgets(answer,sizeof(answer),stdin);
}
while(strcmp(affirmation,answer) == 0);
return 0;
}
I expected from this program to check the divisibility of a provided number by 97 and then to ask if I again want it to check for another number if I input "yes". But it isn't prompting for my input .
If anybody can explain the reason behind this problem and suggest some ways to get through, it will be appreciated.The output is given below:
This output is for num = 194.
It is being executed but what it's reading is the newline character in the input stream that wasn't read when you did the scanf.
What your scanf does is to first skip any white space, then read an integer up to but not including the first non-digit character, which is probably the \n generated by the ENTER key.
Then, because fgets reads a line up to the next newline, you get an empty line.
In both the preceding two paragraphs, I'm assuming for simplicity ideal conditions such as ensuring it's a valid integer and that the line is not longer than the size you provided. Obviously, deviating from that could cause unruly behaviour.
It's generally not a good idea to mix the two input types unless you know in great detail what will be in the input stream. It's often better to just use line-based input and then sscanf that into more appropriate variables.
That way, you can be more sure about where your input stream is at any given point.
A quick fix in this particular case is simply to augment your scanf so that it skips to the start of the next line:
scanf("%d",&num);
while (getchar() != '\n') {}
The other problem you'll have after that is the fact that your fgets input will actually be "yes\n" rather than "yes", so that string comparison won't work.
If you're after a fairly robust line input function that takes care of that (and many other things), see here.

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.

Why does scanf get stuck in an infinite loop on invalid input? [duplicate]

This question already has answers here:
Why is scanf() causing infinite loop in this code?
(16 answers)
Closed 9 years ago.
In line 5 I read an integer and isint is getting 1 if it reads an integer or 0 if it's not an integer. If isint is 0 I have a loop asking user to give an integer and I read until the user gives an integer. I try this code giving a character instead of an integer but I have an infinite loop. The program just doesn't wait to give a new input. What's wrong with my code?
#include <stdio.h>
int main(void) {
int arg1;
//int arg2;
int attacknum = 1;
int isint = 1;
//printf("Insert argument attacks and press 0 when you have done this.\n");
printf("Attack %d\n", attacknum);
attacknum++;
printf("Give attacking argument:");
isint = scanf("%d", &arg1); //line 5
while(isint == 0){
printf("You did not enter a number. Please enter an argument's number\n");
isint = scanf("%d", &arg1);
printf("is int is %d\n", isint);
}
return 0;
}
As others have mentioned, if scanf can't parse the input, it leaves it unscanned.
Generally scanf is a poor choice for interactive input because of this kind of behavior, and because it doesn't match the line-at-a-time interface experienced by the user.
You are better off reading one line into a buffer using fgets. Then parse that line using sscanf. If you don't like the input, throw the whole line away and read another one.
Something like this:
#include <stdio.h>
int main(void)
{
char line[256];
int arg1;
int isint;
while (1) {
printf("Give attacking argument:");
fgets(line, sizeof line, stdin);
isint = sscanf(line, "%d",&arg1);
if (isint) break;
printf("You did not enter a number.Please enter an argument's number\n");
}
printf("Thanks for entering %d\n", arg1);
return 0;
}
(For production code you'll want to handle long lines, check return codes, also check for trailing garbage after the number, etc.)
Actually, an even better approach would be to not use scanf if you just want to read an integer, and instead use strtol. That gives you a handy pointer to the character just after the number, and you can check that it's whitespace or nul.
When scanf is confronted with a non-digit it will not consume any input and return that zero integers were read. The non-digit will stay in the input for the next call to scanf that will behave the same as the first call, etc.
In answer to your question below. You could use fgetc to parse at least one character, but this will give the error messages for every character already typed. Typically I think you want to skip until a newline. To this end you could use fgets as suggested by poolie. Or you could add the following after scanf.
int ch;
if (isint == 0)
while ((ch = fgetc(stdin)) != EOF && ch != '\n')
{
/* Skip characters */
}
P.S: In your case it is probably better to put it just before the first printf in the loop.

scanf ignoring, infinite loop

int flag = 0;
int price = 0;
while (flag==0)
{
printf("\nEnter Product price: ");
scanf("%d",&price);
if (price==0)
printf("input not valid\n");
else
flag=1;
}
When I enter a valid number, the loop ends as expected. But if I enter something that isn't a number, like hello, then the code goes into an infinite loop. It just keeps printing Enter Product price: and input not valid. But it doesn't wait for me to enter a new number. Why is that?
When you enter something that isn't a number, scanf will fail and will leave those characters on the input. So if you enter hello, scanf will see the h, reject it as not valid for a decimal number, and leave it on the input. The next time through the loop, scanf will see the h again, so it just keeps looping forever.
One solution to this problem is to read an entire line of input with fgets and then parse the line with sscanf. That way, if the sscanf fails, nothing is left on the input. The user will have to enter a new line for fgets to read.
Something along these lines:
char buffer[STRING_SIZE];
...
while(...) {
...
fgets(buffer, STRING_SIZE, stdin);
if ( sscanf(buffer, "%d", &price) == 1 )
break; // sscanf succeeded, end the loop
...
}
If you just do a getchar as suggested in another answer, then you might miss the \n character in case the user types something after the number (e.g. a whitespace, possibly followed by other characters).
You should always test the return value of sscanf. It returns the number of conversions assigned, so if the return value isn't the same as the number of conversions requested, it means that the parsing has failed. In this example, there is 1 conversion requested, so sscanf returns 1 when it's successful.
The %d format is for decimals. When scanf fails (something other a decimal is entered) the character that caused it to fail will remain as the input.
Example.
int va;
scanf("%d",&va);
printf("Val %d 1 \n", val);
scanf("%d",&va);
printf("Val %d 2 \n", val);
return 0;
So no conversion occurs.
The scanf function returns the value of the macro EOF if an input failure occurs before
any conversion. Otherwise, the scanf function returns the number of input items
assigned, which can be fewer than provided for, or even zero, in the event of an early
matching failure
7.19.6. The scanf function - JTC1/SC22/WG14 - C
So you should note that scanf returns its own form of notice for success
int scanf(char *format)
so you could have also did the following
do {
printf("Enter Product \n");
}
while (scanf("%d", &sale.m_price) == 1);
if(scanf("%d", &sale.m_price) == 0)
PrintWrongInput();
Also keep in the back of your head to try to stay away from scanf. scanf or scan formatted should not be used for interactive user input. See the C FAQ 12.20
After the first number, a '\n' will be in the input buffer (the return you pressed to input the number), so in the second iteration the scanf call will fail (becouse \n isn't a number), scanf will not remove that \n from the buffer, so in the next iteration it will fail again and so on.
You can fix that by reading the '\n' with a getchar() call after scanf.
The "answers" that say it will because there is a '\n' in the buffer are mistaken -- scanf("%d", ...) skips white space, including newlines.
It goes into an infinite loop if x contains 0 and scanf encounters a non-number (not just whitespace) or EOF because x will stay 0 and there's no way for it to become otherwise. This should be clear from just looking at your code and thinking about what it will do in that case.
It goes into an infinite loop because scanf() will not consumed the input token if match fails. scanf() will try to match the same input again and again. you need to flush the stdin.
if (!scanf("%d", &sale.m_price))
fflush(stdin);
Edit: Back when I first wrote this answer, I was so stupid and ignorant about how scanf() worked.
First of all let me clear something, scanf() is not a broken function, if I don't know how scanf() works and I don't know how to use it, then I probably haven't read the manual for scans() and that cannot be scanf()'s fault.
Second in order to understand what is wrong with your code you need to know how scanf() works.
When you use scanf("%d", &price) in your code, the scanf() tries to read in an integer from the input, but if you enter a non numeric value, scanf() knows it isn't the right data type, so it puts the read input back into the buffer, on the next loop cycle however the invalid input is still in the buffer which will cause scanf() to fail again because the buffer hasn't been emptied, and this cycle goes on forever.
In order to tackle this problem you can use the return value of scanf(), which will be the number of successful inputs read, however you need to discard the invalid inputs by flushing the buffer in order to avoid an infinite loop, the input buffer is flushed when the enter key is pressed, you can do this using the getchar() function to make a pause to get an input, which will require you to press the enter key thus discarding the invalid input, note that, this will not make you press the enter key twice whether or not you entered the correct data type, because the newline character will still be in the buffer. After scanf() has successfully finished reading the integer from input, it will put \n back into the buffer, so getchar() will read it, but since you don't need it, it's safe to discard it:
#include <stdio.h>
int main(void)
{
int flag = 0;
int price = 0;
int status = 0;
while (flag == 0 && status != 1)
{
printf("\nEnter Product price: ");
status = scanf("%d", &price);
getchar();
if (price == 0)
printf("input not valid\n");
else
flag = 1;
}
return 0;
}

Resources