Reputation: 969
I have a "minutes" variable that I'd like the user to enter a positive number into.
int main(void)
{
float minutes;
minutes = -1;
printf("Find out how many bottles worth of water your showers use!\n");
printf("How many minutes do you spend in the shower? ");
scanf("%f", &minutes);
while(minutes < 0)
{
printf("Please enter a positive number: ");
scanf("%f", &minutes);
}
}
It works as intended for numbers. If minutes >= 0, it accepts it, and if minutes < 0 it keeps asking. If I enter a string it infinitely loops
printf("Please enter a positive number: ");
and never gives me a chance to enter a new value. Why is this and how can I fix it? Thanks!
Upvotes: 5
Views: 522
Reputation: 223699
If you don't enter a numerical value, whatever you types stays in the input buffer. You can check for this by reading the return value of scanf
, which tells you the number of items read. If it is 0, you can use getchar
to read characters until the next newline to flush the buffer.
int main(void)
{
int rval, c;
float minutes;
minutes = -1;
printf("Find out how many bottles worth of water your showers use!\n");
printf("How many minutes do you spend in the shower? ");
rval = scanf("%f", &minutes);
if (rval == 0) {
while (((c = getchar()) != '\n') && (c != EOF));
}
while(minutes < 0)
{
printf("Please enter a positive number: ");
rval = scanf("%f", &minutes);
if (rval == 0) {
while (((c = getchar()) != '\n') && (c != EOF));
}
}
}
Upvotes: 6
Reputation: 123458
The %f
conversion specifier tells scanf
to stop reading input as soon as it sees a character that isn't part of a legal floating-point constant (i.e., something that isn't a digit, decimal point, or sign). That bad character gets left in the input stream, so the next call to scanf
fails, and the next, and the next, etc.
You should always check the return value of scanf
- it will tell you how many items were successfully read and assigned from the input stream. In this case, you're expecting a single item, so you should get a return value of 1. If you get a return value of 0, then it means the input is not a proper floating point value, and that bad input has to be cleared somehow. Here's one possible solution:
if ( scanf( "%f", &minutes ) == 1 )
{
// process minutes as normal
}
else
{
// clear everything up to the next whitespace character
while ( !isspace( getchar() ) )
; // empty loop
}
The only problem with this is that scanf
is kind of dumb, and if you type something like 123fgh
, it will convert and assign the 123
while leaving fgh
in the input stream; you would probably want to reject that whole input completely.
One solution is to read the input as text, and then convert it using strtod
:
char buffer[BUFSIZE]; // where BUFSIZE is large enough to handle expected input
...
if ( fgets( buffer, sizeof buffer, stdin ) )
{
char *chk; // chk will point to the first character *not* converted; if
// it's anything other than whitespace or the string terminator,
// then the input was not a valid floating-point value.
double tmp = strtod( buffer, &chk );
if ( isspace( *chk ) || *chk == 0 )
{
minutes = tmp;
}
else
{
// input was not a proper floating point value
}
}
This has the benefit of not leaving crap in the input stream.
Upvotes: 2