Related
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...
Here is something that confused me.
# include <stdio.h>
# include <limits.h>
int main()
{
int a;
int b;
printf("Enter two integers: ");
while(scanf("%d %d", &a, &b)!=2||b==0||a+b>INT_MAX||a*b>INT_MAX){
printf("Invalid entry, please re-enter: ");
while(getchar()=='\n');
}
printf("sum is %d, difference is %d, product is %lf, divid is %.2f, remainder is %d", a+b, a-b, (double)a*b, (float)a/b, a%b);
return 0;
}
With above code, if I enter "a 1" press ENTER:
Invalid entry, please re-enter:
pops up, then I enter "2 B" press ENTER, the last printf will execute.
However, if I change while(getchar()=='\n'); to while(getchar()!='\n');, and with the same entry (I enter "a 1" press ENTER)
Invalid entry, please re-enter:
pops up, then I enter "2 B" press ENTER), the last printf will not execute, and
Invalid entry, please re-enter:
pops up again.
What causes the difference here? How exactly do scanf and getchar work?
In the first case (while (getchar() == '\n');), the getchar() reads the a, but it isn't a newline, so the loop exits, leaving the space and the 1 in the input buffer. The repeated call to scanf() skips white space, reads 1, skips white space (newline included) and reads 2, leaving space and B in the input. Since the loop condition is now terminated (scanf() returned 2), the printf() is executed.
In the second case (while (getchar() != '\n');), this loop reads the a, the blank, the 1, and the newline before stopping. When you type 2 B, there aren't two numbers, so scanf() returns 1 and the main loop continues.
Note that your code will go into an infinite loop if you indicate EOF (usually Control-D on Unix, Control-Z on Windows) — at least, on most systems. You should always consider EOF. In context, you probably need something like:
int rc;
while (((rc = scanf(…)) != 2 && rc != EOF) || …)
You'd test rc against EOF before printing, too.
How do scanf and getchar work in C?
Sadly, they often do not work together well.
Code is a poor example of attempting to consume the rest of the input line when the input text is a problem.
If scanf("%d %d", &a, &b) failed to scan in 2 int, a prompt occurs and input is read with getchar until an Enter or '\n' happens.
If 2 int were scanned, various faulty tests are performed on a,b
// poor code
while(scanf("%d %d", &a, &b)!=2||b==0||a+b>INT_MAX||a*b>INT_MAX){
printf("Invalid entry, please re-enter: ");
while(getchar()=='\n');
}
The problem is that the code certainly intends to read a line of user input and then validate it. Unfortunately, scanf("%d %d" can consume multiple '\n' and leaves stdin with unclear contents when 2 int are not scanned. Code is an infinite loop on end-of-file.
Better to read a line of input with fgets(). Flushing stdout insures the prompt is seen before input is read.
char buf[80];
const char *prompt = "Enter two integers: ";
for (;;) {
fputs(prompt, stdout);
fflush(stdout);
if (fgets(buf, sizeof buf, stdin) == NULL) Handle_EndOfFile_or_Error();
prompt = "Invalid entry, please re-enter: ";
int n;
if (sscanf(buf, "%d%d %n", &a, &b, &n) != 2) continue; // not 2 ints
if (buf[n]) continue; // extra text
if ((a < 0) ? (b < INT_MIN - a) : (b > INT_MAX - a)) continue; // + overflow
... // other tests
else break;
}
other tests
scanf() and getchar() read what is in the input buffer (stdin for standard input). When stdin is empty, the program will wait for the user to write something with the keyboard. When you press ENTER, the text you just wrote is stored in stdin. Then scanf() or getchar() can read something in stdin.
Notice that pressing ENTER will store a '\n' character (newline) in stdin.
A code like while( getchar() != '\n' ); is asking to getchar() to read one character from stdin while the character is not '\n'. It compares the return of getchar() with '\n'. So this is a way to "clean" the input buffer otherwise your program can go crazy.
EDIT
With the code you posted :
scanf() is called, you write "a 1", it tries to read a first integer but it is a character ('a') so it stops reading. printf() ask you to re-enter, then getchar() is called and reads the 'a'. Now scanf() is called a second time, reads the 1, then you have to enter something, you enter "2 B" so the second %d in scanf() can read the 2. Now the return of scanf() is 2, b is not 0 and the others conditions are false so the loop is ending. Notice that the 'B' character is still in stdin :)
I try to summarize what you need to know.
First, about your question. You do know the function of scanf is to read what the input (number) we give right? getchar on the other hand, is like scanf except that it is used for character/sentence only. You cannot use getchar to input number.
Second, there are no problem in your code except that it is okay not to use some line like that getchar line. It is also okay not to use while. Unless you want to make some condition, you may combine while with if / else function.
Third, you cannot input the number like you did: "1 a", "2 b". Those are considered invalid. This is because you are using %d in your scanf which refer to decimal number only. It means, you can input number 1-9 only and not the letters. So when the program asked to enter the 2 integers, you just type for example: 4 5. That's 4, space, 5. That is the correct way.
I have wrote a small code to get value from Fahrenheit to Celsius. I wanted to keep inputting data until I press any other key than 'y'. But this loop doesn't work that way and stops after one iteration.
#include <stdio.h>
int main()
{
char ch='y';
int far, cen;
do {
printf("again\n");
scanf("%d",&far);
//cen = (5.0/9.0)*(far-32);//integer division will truncate to zero so we can make 5/9 to 5.0 / 9.0
cen = (5*(far-32))/9;//or this way we can use this formula
printf("\n%d\t%d",far, cen);
printf("ch=%c",ch);
scanf("%c",&ch);
}while(ch == 'y');
return 0;
}
What is the problem here?
P.S
I added a line and made a new code like this
#include <stdio.h>
int main()
{
char ch='y';
int far, cen;
do {
printf("again\n");
scanf("%d",&far);//here we press carriage return. this value is in stdin
//cen = (5.0/9.0)*(far-32);//integer division will truncate to zero so we can make 5/9 to 5.0 / 9.0
cen = (5*(far-32))/9;//or this way we can use this formula
printf("\n%d\t%d",far, cen);
scanf("%c",&ch);//putting a space before %c makes the newline to be consumed and now it will work well
if((ch == '\r')|| (ch == '\n'))
printf("1\n");
printf("ch=%c",ch);//this takes the carriage return in stdin buffer
}while(ch == 'y');
return 0;
}
I need to know carriage return here is \r or \n?
When the value for scanf("%d",&far); is entered and press enter, the scanf stores the carriage return in the buffer. When it encounters the second scanf in the code scanf("%c",&ch); it takes the carriage return present in the buffer as the input to 'ch'. So it doesn't wait for the user input.
Please have a look at the post here
As indicated in one of the reply the solution is to put a space in scanf
scanf(" %c",&ch);
You should always check the return value of scanf. Your first use of scanf may fail if the user does not enter a valid integer, in which case, you are using far without initialising it (which is undefined behaviour). scanf returns the number of items that were successfully scanned. If you are requesting scanf to scan one integer, then it should return 1 if it successfully managed to scan an integer.
int scanresult = scanf("%d", &far);
if (scanresult != 1)
{
puts("Invalid input or unexpected end of input");
return 1;
}
In addition, the %c conversion specifier is unique in that it does not cause scanf to gobble up any preceding whitespace unlike the other conversion specifiers. To force scanf to gobble up the whitespace (such as linefeeds, carriage returns, spaces, tabs etc), simply put a space character before the %c, e.g.
scanresult = scanf(" %c", &ch);
For scanf, the space character is actually a directive to parse and skip all whitespace.
This is because of the previous newline character remaining in the buffer. You can simply replace scanf by this line:
while((ch = getchar()) == '\n');
You'll be needing the same technique in combination with ungetc() in many occasions.
Add fflush() function, just above scanf("%c", &ch). Because buffer of CONSOLE INPUT stores characters that not returned to program. Which is ENTER pressed in previous scanf:
#include <stdio.h>
int main() {
char ch='y';
int far, cen;
do {
printf("again\n");
scanf("%d",&far);
//cen = (5.0/9.0)*(far-32);//integer division will truncate to zero so we can make 5/9 to 5.0 / 9.0
cen = (5*(far-32))/9;//or this way we can use this formula
printf("\n%d\t%d",far, cen);
printf("ch=%c",ch);
scanf("%c",&ch); // This scanf will be ignored, because loads last
// character from buffer that can be recognized
// by scanf which is pressed "ENTER" from previous scanf
printf("%d", ch) // Shows 10, which is ASCII code of newline
fflush(stdin); // Clear buffer
scanf("%c",&ch); // Now it will prompt you to type your character.
// printf("%c"ch); //Without fflush, it must show 10, which is \n code
}while(ch == 'y');
return 0;
}
if after Y you press "space" or "return" this is the character you will find in %C
I dont really uderstand the code below. How does it work exactly (I/O buffers I mean). I dont need a \ncharacter in my code an it still works! Can anyone explain it to me step by step?
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int x = -1;
do
{
printf("Give x: ");
scanf("%d", &x);
}while(x<=0);
printf("x = %d\n", x);
x = -1;
while(x<=0)
{
printf("Give x: ");
scanf("%d", &x);
}
printf("x = %d\n", x);
return 0;
}
According to scanf's documentation on cplusplus.com:
Whitespace character: the function will read and ignore any whitespace characters
encountered before the next non-whitespace character (whitespace characters include
spaces, newline and tab characters -- see isspace). A single whitespace in the format
string validates any quantity of whitespace characters extracted from the stream
including none).
This is why you don't need to specify the \n in scanf the next scanf call will simply ignore it.
Stripping away the parts OP likely understand, look at the scanf() calls.
The "%d" format specifier says to scan though optional whitespace (space, tab, \n, etc.) and then scan an int. This typically continues until a character is encountered that does not belong to the int. Then that character is "ungotten" (put back in the input buffer).
Say your input was " 123 -456". After the first while loop " -456" would remain in the input buffer. The second while loop would consume the " -456". Assuming that stdin was closed after the -456, scanf() would then detect there is no more data and set x to the value -456. As x is still negative, the second while loop performs scanf() again. This time, no data and scanf() does not change x and returns EOF, which sadly is not monitored. Result: endless loop.
Now try " 123a 456". After the first while loop "a 456" would remain in the input buffer. The second while loop would call scanf() and fail to convert anything as a does not begin a number - thus x remains -1. scanf() would return 0, which unfortunately is not monitoring. The a, not begin consumed, would remain in the input buffer. The 2nd while loop again calls scanf() which would do the same thing resulting in an endless loop.
do {
...
scanf("%d", &x);
} while (x<=0);
...
x = -1;
while (x<=0) {
...
scanf("%d", &x);
}
Far better to use fgets()/sscanf() pair for input from user input.
(User input is evil!)
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;
}