I have a piece of code which just asks for user input and keeps prompting user for input if the user entered 0 or less.
float n;
do {
printf("Enter a number: ");
scanf_s("%f", &n);
} while (n <= 0);
but when the user enters a Char it keeps executing printf() forever without prompting or waiting for user inputs.
I've used visual studio debuger with breakpoints and found that scanf() is executed without the user pressing any keys so i have no idea what's going on here.
As you may or may not know, scanf_s() returns the number of items that were successfuly converted and assigned to variables. However, when no items are converted and assigned successfully, it returns 0.
In your case, scanf_s() expects a float. So, when a char is entered instead, it doesn't match the conversion specifier and fails. As a result, scanf_s() returns 0.
In addition, the character isn't consumed and remains in the input buffer. So scanf_s() will continue to fail on that same character and loop infinitely.
Solution
1) Check the return value of scanf_s() for a failure.
2) Consume any unread characters when there's a failure.
Suggestions
1) Initialize your variable n, since it will be evaluated even when scanf_s() fails.
2) Since you want the user to enter a number greater than 0, state it specifically when prompting for one.
Code
float n = 0; /* initialize the variable */
do {
printf("Enter a number greater than 0: ");
if (scanf_s("%f", &n, 1) != 1)
while (getchar() != '\n'); /* consume any unread characters */
} while (n <= 0);
Related
I am using the following code to scan for integers input from the user until end of line occurs.
while (scanf(" %d", &num) != EOF) {
printf("Do something")
}
This works as expected until the user inputs a string instead of an integer. The program would then endlessly keep printing Do something. Why is that happening?
How can I stop the loop only when End of line occurs, but ignore string inputs and only perform my logic if integer inputs have occured?
scanf() returns the number of input items successfully assigned. That is, in your example, 1 if a number is entered, or 0 otherwise. (Unless an input error occurs prior to the first input item, in which case it returns EOF.)
In case a string is entered, this fails to match %d, scanf() returns zero, the loop is entered, "Do something" is printed, and scanf() is called again.
But the string has not been consumed by any input function.
So the string fails to match, "Do something" is printed... you get the idea.
Be happy you do not access num, because if you haven't initialized that beforehand, accessing it would be undefined behaviour (as it still isn't initialized)...
Generally speaking, do not use scanf() on potentially malformed (user) input. By preference, read whole lines of user input with fgets() and then parse them in-memory with e.g. strtol(), strtof(), strtok() or whatever is appropriate -- this allows you to backtrack, identify exactly the point where the input failed to meet your expectations, and print meaningful error messages including the full input.
How can I stop the loop only when End of line occurs, but ignore string inputs and only perform my logic if integer inputs have occured?
When scanf(" %d", &num) returns 0, read a single character and toss it.
int count;
while ((count = scanf("%d", &num)) != EOF) {
if (count > 0) printf("Do something with %d\n", num);
else getchar();
}
I'm trying to make a program where the user inputs value to an array. What is actually required is that the program should validate against a char character. So if the user inputs a random char such as 'n' the program should tell him "You introduced a char, please input an integer: ".
How is that possible to make that without using a char variable?
for (i = 1; i <= size; i++) {
printf("Introduce the value #%d of the list: ", i);
scanf("%d", &list[i]);
if () { // I'm blocked right in this line of code.
printf("What you tried to introduce is a char, please input an integer: ");
scanf("%d", &list[i]);
}
Thanks in advance.
As #MFisherKDX says, check the return value of scanf. From the scanf man page:
These functions return the number of input items successfully matched
and assigned, which can be fewer than provided for, or even zero in
the event of an early matching failure.
The value EOF is returned if the end of input is reached before either
the first successful conversion or a matching failure occurs. EOF is
also returned if a read error occurs, in which case the error
indicator for the stream (see ferror(3)) is set, and errno is set
indicate the error.
So capturing the return value of scanf in an int variable and then comparing that variable to 1 (in your case, because you are only attempting to read 1 item) should tell you if scanf successfully read an integer value.
However, there is a nasty pitfall when using scanf that you should be aware of. If you do type n at the prompt, scanf will fail and return 0, but it will also not consume the input you typed. Which means that the next time you call scanf, it will read the same input (the n character you typed), and fail again. And it will keep doing so no matter how many times you call scanf. It always amazes me that computer science educators continue to teach scanf to students, given not only this potential pitfall, but several other pitfalls as well. I wish I had a nickel for every hour that some CS student somewhere has spent struggling to get scanf to behave the way their intuition tells them it should. I'd be retired on my own private island by now. But I digress.
One way around this particular pitfall is to check if scanf failed, and if so, to purposely consume and discard all input from stdin up to and including the next newline character or EOF, whichever comes first.
First let's look at some unfixed code that causes an infinite loop if you enter a non-integer as input:
// Typing the letter 'n' and hitting <Enter> here causes an infinite loop:
int num, status;
while (1) {
printf("Enter a number: ");
status = scanf("%d", &num);
if (status == 1)
printf("OK\n");
else
printf("Invalid number\n");
}
The above code will (after you type n and hit <Enter>), will enter an infinite loop, and just start spewing "Invalid number" over and over. Again, this is because the n you entered never gets cleared out of the input buffer.
There are a few possible ways to get around this problem, but the consensus seems to be that the most portable and reliable way to do so is as follows:
// Fixed. No more infinite loop.
int num, status;
while (1) {
printf("Enter a number: ");
status = scanf("%d", &num);
if (status == 1)
printf("OK\n");
else {
printf("Invalid number\n");
// Consume the bad input, so it doesn't keep getting re-read by scanf
int ch;
while ((ch = getchar()) != '\n' && ch != EOF) ;
if (ch == EOF) break;
}
}
The function scanf() will returns the number of elements read, so in this case it will return 1 every time it reads an int and 0 when it reads a char, so you just need to verify that return value.
Keep in mind that after reading a character it will remain in the buffer so if you use the scanf() command again it will read the character again and repeat the error. To avoid that you need to consume the character with while(getchar() != '\n');
With that in mind I modified your code so that it works properly printing an error message if a character is introduced and asking for a new int.
for (int i = 1; i <= size; i++) {
printf("Introduce the value #%d of the list: ", i);
while (!scanf("%d", &list[i])) { //verifies the return of scanf
while(getchar() != '\n'); //consumes the character in case of error
printf("What you tried to introduce is a char\n");
printf("please introduce the value #%d of the list: ", i);
}
}
I'm new to C programming and i find prompting for user input quite a challenge for beginners like me.
I'm just writing a simple C code to continuously prompt for user input until the user enters a negative number which stops the program. In a way, i want the format of prompting user input similar to the way we used to do in python where:
Enter a number: (userinput a number here and press enter)
and the output will be:
The number you entered is 10 (for example)
which doesn't yet stop the loop and prompts for another number since no negative number is entered.
Enter a number: (userinput another number which is negative)
Output:
The number you entered is -10 (for example)
and the program stops.
I tried writing in C:
#include <stdio.h>
int value = 0;
while (value >= 0) {
do {
printf("Enter a number: ");
scanf("%d",&value);
printf("The number you entered is %d", &value);
}
but i can't seem to get the "Enter a number:" statement to display first when i run the program as it immediately request for user input at the start without the prompting message displayed until when i enter an integer, the prompting message displays which is the opposite of what i wanted. Would appreciate some help on this.
One of the biggest problems faced by new C programmers is handling user input correctly, especially when using the scanf family of functions. Why? When using scanf you must account for all characters left in the input buffer (stdin here) in the event of a matching failure. Why? When a matching failure occurs, scanf stops processing characters at the point of failure, and the character(s) that caused the failure remain in your input buffer unread, just waiting to bite you on your next attempted input.
Further complicating the issue is how the difference scanf conversion specifiers treat whitespace. Your numeric input specifiers and %s will consume leading whitespace, while the remainder of the conversion specifiers don't. That means if you are taking input with other than a numeric or %s conversion specifier -- you must account for and remove the trailing '\n' before attempting the next character or character class conversion.
That said, whenever you use scanf, it is up to you to check the return so you can determine whether the user canceled input with a manually generated EOF of whether a matching or input failure occurred.
At the bare-minimum, you must check the return and handle any return indicating less than the number of expected conversions took place. In your case, with one conversion to int, you must check that the return is 1 before making use of value. Otherwise you can easily invoke Undefined Behavior. Now just checking whether all conversions occurred does not allow you to discriminate between EOF or matching failure - leaving you without the information needed to proceed making the best you can do is to exit on invalid input, e.g.
#include <stdio.h>
int main (void) {
int value = 0;
while (value >= 0) {
printf("Enter a number: ");
if (scanf("%d",&value) != 1) {
fputs ("error: invalid input\n", stderr);
return 1;
}
printf("The number you entered is %d\n", value);
}
return 0;
}
Example Use/Output
$ ./bin/scanfpos2
Enter a number: 1
The number you entered is 1
Enter a number: 10
The number you entered is 10
Enter a number: foo
error: invalid input
(note: on entry of an invalid integer, all the program can do is end, you do not know whether the input was canceled or whether a matching failure occurred which would allow you to take appropriate action to continue by emptying the input buffer if the failure was a matching failure.)
The proper way to handle input with scanf is to cover all potential error conditions and to gracefully respond to a matching failure by clearing the input buffer of the offending characters allowing you to continue with input. It helps to have a small helper-function to clear stdin rather than having to repeatedly include a clearing loop every where scanf is used in your code. A short example would be:
/** remove all characters that remain in stdin */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
Which simply reads all characters from stdin until the '\n' or EOF is encountered. Combining that with an expanded check of the return of scanf will allow you to handle a matching failure gracefully, e.g.
#include <stdio.h>
/** remove all characters that remain in stdin */
void empty_stdin (void)
{
int c = getchar();
while (c != '\n' && c != EOF)
c = getchar();
}
int main (void) {
int value = 0;
while (value >= 0) {
int rtn; /* variable to store return on scanf */
printf ("Enter a number: ");
rtn = scanf ("%d",&value);
if (rtn == EOF) { /* handle EOF */
fputs ("user canceled input.\n", stderr);
break;
}
else if (rtn == 0) { /* handle matching/input failure */
fputs ("error: invalid input\n", stderr);
empty_stdin();
}
else /* good input - output value */
printf("The number you entered is %d\n", value);
}
return 0;
}
Example Use/Output
$ ./bin/scanfpos2
Enter a number: 1
The number you entered is 1
Enter a number: 10
The number you entered is 10
Enter a number: foo
error: invalid input
Enter a number: -1
The number you entered is -1
Here if a non-integer like foo is entered, it is caught, the characters removed from stdin and the loop prompts again for input.
Look things over. C is not python. Python hides much of the implementation details from you to essentially protect you from instances just as this, but it has its drawbacks as well. With C, nothing is hidden from you. You are given free reign to write to memory you don't own, repeatedly attempt to read form an input buffer after a conversion failure, etc.. You are responsible for the details.
Lastly, all of this is the primary reason taking input with fgets or POSIX getline is recommended for new users. With a sufficiently sized buffer (don't skimp on size), fgets will read a line at a time from the input buffer, preventing offending characters remaining just waiting to bite you again. getline will allocate a buffer of sufficient size no matter how long the line is -- but you are responsible for freeing the memory when you are done with it. Investigate both as alternatives to using scanf. You can always call sscanf on the buffer holding the line after it is read to parse numeric values from it.
I have a program where I want the input integer to be between 2 and 64 inclusive, so I put scanf inside a do { ... } while loop. Here's the code I initially tested:
int initialBase;
do {
printf("Initial base: ");
scanf("%i", &initialBase);
} while (initialBase < 2 || initialBase > 64);
The problem is whenever the input is not a valid integer, it just outputs the printf statement indefinitely and no longer prompts for user input, instantly flooding the console. Why is that happening and what's a better way of reading input that satisfies the conditions I want?
When scanf() fails, the argument is not automatically initialized, and uninitialized values could be any value, so it might be less than 2 or greater than 64 no one knows.
Try this
int initialBase;
/* some default value would be good. */
initialBase = 2;
do {
printf("Initial base: ");
if (scanf("%i", &initialBase) != 1)
break;
} while ((initialBase < 2) || (initialBase > 64));
the check will break out of the loop if you input something that is not a number, the initialiazation of initialBase is just a good habit which in your case could have prevented the behavior you describe, but in this case it's there to prevent accessing an uninitialized value after the while loop.
The reason the loop didn't stop, was because scanf() leaves some characters in the input stream when they are not matched, and calling scanf() again while those characters are still there will make scanf() keep waiting for valid input, but returning immediatly with the currently invalid input that is in the stream, if you want to keep reading, try reading characters from the stream until a '\n' is found, this way
int initialBase;
initialBase = 0;
do {
printf("Initial base: ");
if (scanf("%i", &initialBase) != 1)
{
while (fgetc(stdin) != '\n');
continue;
}
} while ((initialBase < 2) || (initialBase > 64));
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;
}