How to handle less input than expected with scanf - C - c

I need to receive from the user input that can come in 2 forms, char int float or char int, but I don't know which of them will be given. I tried this:
int main(){
char letter;
int num;
float value;
while(getchar()!=EOF){
scanf(" %c %d %f", &letter, &num, &value);
printf("%c\n", letter);
printf("%d\n", num);
printf("%f\n", value);
}
return 0;
}
The problem with this is when I give an input like this:
? 12345
g 12345 45.6
? 12345
Output given: Expected Output:
1 ?
2345 12345
0.000000 0.000000
1 g
2345 12345
45.599998 45.599998
? ?
12345 12345
45.599998 45.599998
Why is part of the number going in the place of the char and the char is ignored? Is there a way to fix this?

There are several problems here:
The missing char. When you do while(getchar()!=EOF) for the first time, you are consuming the first character in the stream, which happens to be '?'. The subsequent scanf() cannot retrieve that anymore, so it grabs the next non-whitespace character, which happens to be the '1' of the following number.
After that '1' is read, the rest of the number is parsed, providing the result 2345.
The following conversion tries to parse a float, but the next non-whitespace character is a g, so no float can be read, and the conversion fails.
You fail to check, how many conversions are successful. scanf() dutifully returns the number of successful conversions, but you ignore that.
If you had checked the return value, you would have found that the first scanf() call returns 2 because the character and the integer conversions finished successfully, but the float conversion failed.
Likewise, the second scanf() call should have returned 3, signaling that all three conversions succeeded. The last call to scanf() should return 2 again.
The gobbling up of a character in while(getchar()!=EOF) bites you again on the second line, this time it is the 'g' that's removed from the stream before the scanf() gets a chance to read it.
After the second call to scanf() successfully terminates, the '\n' character is left in the stream. Thus, the following while(getchar()!=EOF) gobbles that up, and the third scanf() call can finally correctly fetch the first character on the line ('?').
The float conversion fails again on the third input line, and the corresponding variable is left untouched, and you fail to detect this condition by ignoring the return value of scanf().
Long story short:
Each getchar() call consumes a character that cannot be read by scanf() anymore.
Any scanf() call that ignores the return value is a bug. Without checking that, you cannot know that you actually read anything.
Btw: man scanf and man getchar could have told you everything I just said. It pays off to know how to read manpages.

In this case, it's best to first retrieve the user input as char* (fgets-like) and then parse it with sscanf (not scanf, sscanf).
You will have to check the return value of sscanf in order to know wether the input user is correctly formatted.
If you need to check if there is not garbage value after the parsing (like "g 12345 45.6 garbagevalue"), you can add a %c after and check if the value have changed.
char letter;
int integerNumber;
double floatingNumber;
char end;
char *userInput = ...;
end = '\0';
if (sscanf(userInput, "%c %d %f%c", &letter, &integerNumber, &floatingNumber, &end) == 3 && end == '\0') {
// First form
} else {
end = '\0';
if (sscanf(userInput, "%c %d%c", &letter, &integerNumber, &end) == 2 && end == '\0') {
// Second form
} else {
// Error : Input not strictly "%c %d %f" or "%c %d"
}
}
Sound good to you ?

I wonder if it would be simpler to read in a block of data from the keyboard to a string from the keyboard and then analyse with sscanf as in the outline below
#define MAX_INPUT 100
char data_input[MAX_INPUT];
int finished=0, test;
char letter;
int num;
float value;
while (finished==0) {
// scanf("%s",data_input); //taken out after useful comments below
fgets(data_input,100,stdin); //replacement from comments...
// insert code using sscanf to read in data from user
test=sscanf(data_input, "%c %d %f", &letter, &num, &value);
if (test==3)
{
//success - sscanf successfully assigned 3 values...
// (ony if you have everything you need then....)
finished =1;
} else if (test==2){
// insert something to try to test if data from two data entries is ok....
// (ony if you have everything you need then....)
finished =1;
} else {
printf("problem with data entry - please type again\n");
}
}
with apologies to #Tom's because the use of sscanf was first suggested by him...

Related

Why does scanf first return 1 and then 0 when it gets decimal input - but expects integer?

Why does the following code execute - when a decimal number is entered - at first, but on the second iteration scanf directly returns 0, and the loop breaks?
#include <stdio.h>
int main(){
int num = 1;
while(num != 0){
printf("input integer: ");
if(scanf("%d", &num) == 0)
break;
printf("out: %d\n", num);
}
return 0;
}
If I enter, for example, 5.55, it prints 5 (scanf returns 1 and printf executes), however on the next iteration it [scanf] returns 0 and the loop breaks. Why does scanf return 1 in the first place?
Depending on the format specifier in your format string, scanf consumes as much input from input stream as matches for the specified argument type.
For %s it reads until it sees a space or end of line.
For %d it reads as long as it sees digits.
If you use %d and then enter "5.55" in stdin, scanf will only consume the first '5' and the remaining ".55" will stay in the buffer.
On your first call, scanf is able to convert input for the first parameter. As a result, it will return 1 and the value 5 is assigned to num.
The data in input buffer does not go away between calls to scanf.
In your second call you also use %d but now, scanf sees a '.' that does not match the required format. As a result, no input is consumed, no value is converted and the return value is 0.
This will stay the same for any following call until you consume input in another way.

Repeat the last line of the input using EOF(CTRL+D)

When I put the values of my input and type ctrl + d the last line repeat and that is not suppost to happen. Anyone know what is happen?
while(1) {
if(getchar()==EOF)
break;
scanf(" %c %d", &s, &b)
}
For example, image that I put (input)m 2 and the result of that is 5(output) if I click ctrl+d to finish the program the 5 is printed again and I don't want that.
scanf("%c %d",&s,&b); stops at the newline after your input, but it doesn't remove it from the input stream. So the next call to getchar() returns that newline character, and doesn't return EOF.
Then you call scanf() again, but don't check the result. There's nothing left for it to read, so it leaves the variables s and b unchanged, and you print that again.
Then you return to the top of the loop. Now getchar() returns EOF.
Change your loop condition to:
while (scanf(" %c %d", &s, &b))
and get rid of the getchar() check.
The space before %c will make it skip over any whitespace before the character.
Never assume that scanf succeeded. Check its return value to make sure that it was able to read everything that you expect it to.
int n = scanf("%c %d",&s,&b);
if ( n != 2 )
{
// deal with the error.
}
else
{
// Use the data
}

Why doesn't scanf() wait for the next input if I previously entered a certain input

below is my simple code to enter a number and print it. it is inside a while(1) loop so i need to "Enter the number infinite number of time- each time it will print the number and again wait for the input".
#include<stdio.h>
int main()
{
int i;
while(1){
printf("\nenter i \n");
scanf("%d", &i);
if(i==1)
{
printf("%d \n", i);
}
}
return 0;
}
it was working fine. but suddenly i noticed that IF i ENTER a character(eg: "w") instead of number , from there it won't ask for input!!!**
it continuesly prints,
enter i
1
enter i
1
......
when i debug using GDB, i noticed that after i enter "w", that value of character "w" is not stored in &i . before i enter "w" it had 0x00000001 so that "1" is printed through out the process.
Why it doesn't ask for another input? According to my knowledge, when I enter "w" the ascii value of "w" should be stored in &i. But it doesn't happen.
If I put, "int i; " inside while loop it works fine! Why?
Please test my code in following way:
Copy and paste and run it
When "enter i" prompt will come enter 1
Second time enter "w". See what happens...
scanf with %d format specifier will read everything that "looks like a number", i.e. what satisfies the strictly defined format for a decimal representation of an integer: some optional whitespace followed by an optional sign followed by a sequence of digits. Once it encounters a character that cannot possibly be a part of a decimal representation, scanf stops reading and leaves the rest of the input data in the input stream untouched (to wait for the next scanf). If you enter just w, your scanf will find nothing that "looks like a number". It will read nothing. Instead it will report failure through its return value. Meanwhile your w will remain in the input stream, unread. The next time you try your scanf, exactly the same thing will happen again. And again, and again, and again... That w will sit in the input stream forever, causing each of your scanf calls to fail immediately and your loop to run forever (unless your uninitialized variable i by pure chance happens to start its life with the value of 1 in it).
Your assumption that entering w should make scanf to read ASCII code of w is completely incorrect. This sounds close to what %c format specifier would do, but this is not even close to what %d format specifier does. %d does not read arbitrary characters as ASCII codes.
Note also that every time you attempt to call that scanf with w sitting in the input stream, your scanf fails and leaves the value of i unchanged. If you declare your i inside the loop, the value of i will remain uninitialized and unpredictable after each unsuccessful scanf attempt. In that case the behavior of your program is undefined. It might even produce an illusion of "working fine" (whatever you might understand under that in this case).
You need to check the return value of scanf as well, as it will return the number of successfully scanned and parsed values. If it returns zero (or EOF) then you should exit the loop.
What happens when you enter e.g. the character 'w' instead of a number is that the scanf function will fail with the scanning and parsing, and return zero. But the input will not be removed from the input buffer (because it was not read), so in the next loop scanf will again read the non-numeric input and fail, and it will do this infinitely.
You can try this workaround:
int main()
{
int i;
char c;
while (1)
{
printf("enter i: ");
if (scanf("%d",&i) == 0)
scanf("%c",&c); // catch an erroneous input
else
printf("%d\n",i);
}
return 0;
}
BTW, when were you planning to break out of that (currently infinite) loop?
You need to read up on scanf(), since you seem to be basing your program around some assumptions which are wrong.
It won't parse the character since the conversion format specifier %d means "decimal integer".
Also, note that you must check the return value since I/O can fail. When you enter something which doesn't match the conversion specifier, scanf() fails to parse it.
You would probably be better of reading whole lines using fgets(), then using e.g. sscanf() to parse the line. It's much easier to get robust input-reading that way.
scanf return type can be checked and based on that inputs can be consumed using getchar to solve your problem.
Example code
int main()
{
int i;
int ch;
while(1){
printf("\nenter i \n");
if ( scanf("%d", &i) !=1 )
{
/*consume the non-numeric characters*/
for (; (ch = getchar()) != EOF && ch != '\n'; ) { }
}
if(i==1)
{
printf("%d \n", i);
}
}
return 0;
}
Description:
When scanf("%d", &i) encounters the character, it will not read it. The character will still remains in the input stream. So to consume those characters, getchar() can used. Then scanf will wait for the next input in further iteration.

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;
}

Problem while scanning a char and float simultaneously

I am trying to take five character and 5 float input.
main()
{
char c[5];
float q[5];
int i;
for(i=0;i<5;i++)
{
printf("\n%d ",i);
scanf("%c",c+i);
scanf("%f",q+i);
}
}
But the output is absurd. After two sequential scans, it skips third scan and then again skips fifth scan.
I am not able to understand why is it showing such a behaviour.
I am working on gcc compiler.
Use of scanf is not recommended because of problems like this.
Instead use fgets to read the entire line and then use sscanf to extract what you want(a char or a float) from the line just read:
char line[MAX];
for(i=0;i<5;i++)
{
if( fgets(line,MAX,stdin) && sscanf(line,"%c", c+i)!=1 )
*(c+i) = 0;
if( fgets(line,MAX,stdin) && sscanf(line,"%f", q+i)!=1 )
*(q+i) = 0.0;
printf("%c %f\n",*(c+i),*(q+i));
}
To directly answer why the 3rd and every other scan "skips", it is the way scanf() and the %c format works. When there is a call to scanf(), you typically have to press enter to "submit" the input. Consequently that inserts a newline character into the stdin stream.
When the previous float scan got inputted, the newline is still left in the stream. When the character scan gets reached, that remaining newline character is read in since it fits effectively "skipping" the call.
You should use fgets() with sscanf() as codaddict suggests.
But as a quick fix, you could try adding a call to getchar() after the float scan to consume that newline character from the stream.
edit:
You're saying this doesn't work? (assuming you input the correct kinds of values, one per scanf call)
main()
{
char c[5];
float q[5];
int i;
for(i=0;i<5;i++)
{
printf("\n%d ",i);
scanf("%c",c+i);
scanf("%f",q+i);
getchar();
}
}
You should try this:
int main(){
char c[6];//char array size must be 6 to store five charecter
//as null-terminator('\0')will use the last one
float q[5];
int i;
for(i=0;i<5;i++){
printf("\n%d\n",i);fflush(stdout);
scanf("%c",&c[i]);fflush(stdin);//fflush(stdin) to clear input stream that
//next scanf() won't skip
scanf("%f",&q[i]);fflush(stdin);//fflush(stdin) to clear input stream that
//scanf() won't skip at new loop
}
return 0;
}
fflush() is not defined on an input stream, like stdin. Don't do it.
If you want to "read and discard until newline", then do:
int ch;
do {
ch = getchar();
} while (ch != EOF && ch != '\n');
Note that %c means "read the next character in the input stream, even if it's whitespace, then stop". %f means "read and discard whitespace, then try to read a float from the input stream, then stop."
Your code should be like this :
main()
{
char c[5];
float q[5];
int i;
for(i=0;i<5;i++)
{
printf("\n%d ",i);
scanf("%c",c+i);
while (getchar()!='\n');
scanf("%f",q+i);
while (getchar()!='\n');
}
}
the sentence while (getchar()!='\n'); search till the end of input, so it would not take '\n' as an input value for q+i.Also another while (getchar()!='\n'); after scanf q+i,since you use loop.
Problem in scanning a char value after a float value.
Solution is very simple!!!
instead of writting your code like this
scanf("%c",&...)
try this,
scanf(" %c",&...)
A Space before the"%c" will resolve the problem by neglecting the value of the Return(Enter) key when pressed after typing the value of the float as an input.
When a float value is scanned before a character value.
The value obtained by pressing the Return(Enter) key is collected in the following char variable. Using a space before(" %c",&...) discards the value collected of the Return(Enter) Key, Causing the Char value to be scanned in the next line. Thus solving The Scanning Float-Char problem.

Resources