简体   繁体   中英

scanf issue: input of integer followed by character, ex: 1p, for a single input is treated as 2 inputs

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM