Reputation: 95
NB: to avoid misunderstanding, the code is aimed to handle input errors .. i'm asking for integers (no characters) but the user might enter characters by mistake, so how to avoid the following:
I know that scanf sucks but I have to use it for some reasons. So, the problem I'm facing right now is that if I'm scanning a single integer from the user as follow:
scanf("%d",&c);
if the user enters a digit followed by a character, such as: 1k, it's treated as double input not a single one, and checking the return value for the scanf and using flushinput concept doesn't work here. To make my question clearer:
The user is prompted to enter a choice, and in case of invalid choice he'd be asked again (through a loop), so, if he enters for ex:
k, gives the message (it's an invalid input) once, and rescans
k2, gives the message (it's an invalid input) once, and rescans
2k, gives the message (it's an invalid input) TWICE then rescans.
Any hints to solve that issue? Thanks in advance !!
NB: I check the returned value of scanf as follow:
if (scanf("%d",&confirm)!=1)
{
flushInput(); confirm=0;
}
where:
void flushInput()///prevents infinite loops resulted due to character input instead of integer
{
int c; //c ->absorbs the infinite characters resulted due to the entry of chars, using getchar()
while((c = getchar()) != EOF && c != '\n');
}
Upvotes: 0
Views: 214
Reputation: 17638
The following addresses the question with OP's given constraint of using scanf
. The preferred choice, however, would be to read entire lines, then parse the strings with safer functions like strtol
which perform proper range checking. More about that at atoi vs atol vs strtol vs strtoul vs sscanf.
Using scanf
, the sample code below reads one integer per line of input, while rejecting lines that contain any non-whitespace other than the integer itself.
#include <stdio.h>
#include <string.h>
int scanf_solo_int(int *p)
{
// read integer
int ret = scanf("%d", p);
if(ret == EOF) return -1;
// read rest of line
char s[132 + 1];
if(scanf("%132[^\n]", s) != 1) s[0] = '\0';
scanf("%*1[\n]");
if(ret == 1)
{
// check for extra non-whitespace
if(s[strspn(s, " \t\n")] != '\0') ret = 2;
}
return ret;
}
int main()
{
for(;;)
{
int n;
switch(scanf_solo_int(&n))
{
case 1: printf("ok: %d\n", n); continue;
case 0: printf("error: not a number\n"); continue;
case 2: printf("error: extra characters past %d\n", n); continue;
case -1: printf("error: EOF\n"); break;
default: printf("error: unexpected\n"); break;
}
break;
}
return 0;
}
input output
----------- ------------------------------
12 ok: 12
-34 ok: -34
5 6 error: extra characters past 5
x y z error: not a number
7x error: extra characters past 7
8 x error: extra characters past 8
9.0 error: extra characters past 9
2147483647 ok: 2147483647
-2147483648 ok: -2147483648
9876543210 ok: 1286608618
error: EOF
The last line is an example of uncaught integer overflow where scanf
accepts the 9876543210
string as %d
input, but truncates it mod 2^32
to 1286608618
without warning.
Upvotes: 2