0
votes

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');
  }
1
%d is for reading numbers(ints) why are you giving char type inputIrAM
for better answers provide minimal reproducible programIrAM
When you say you "have to use scanf for some reason", I wonder what that really means? And is the code you're writing part of a learning exercise for school, or as production code for some company? The fact is that scanf is almost useless, and it is in fact completely useless for robust error checking, and it is also completely useless for user input in any kind of commercial or production code.Steve Summit
If you want to do robust, error-checked input, you either can't do it at all using scanf, or it will take you five times as long (and deliver at most 90% of the error checking) as if you used proper, non-scanf methods. Sorry to say this, because it's not answering the question, and I know you said you need to use scanf, but it's really not a reasonable requirement. If you told me I had to do robust input and I had to use scanf, I would either (a) resign or (b) read whole lines using %[^\n], then parse them sanely (that is, just as I would do if I were allowed to call fgets).Steve Summit
If this is part of a class you're taking, and your instructor is insisting that you (a) implement robust error checking using (b) scanf, it is a pointless exercise from which you will learn nothing useful, except perhaps forbearance. Yes, in the real world, it's important for programs to do robust, error-checked input, but in the real world, nobody implements this using scanf.Steve Summit

1 Answers

2
votes

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

Sample run:

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.