tags:

views:

4643

answers:

6

How can I validate the user input by using scanf. Right now I have something like this, but doesn't work.

NOTE: I have the atoi just to validate that the scanf validation works.

scanf("%[0987654321.-]s",buf);

i = atoi(buf);

if(i)
    index = i;
A: 

If you're trying to read a number only, use:

int i;
scanf( "%d", &i );

If you need to do more complicated checking, you'll have to do it yourself. Scanf won't do it for you.

SoapBox
A: 

What you specified in the first argument to scanf are conversion specifiers. scanf will try to convert the input to the format but sometimes you might be surprised what it will match against :)

The first check you should do is on the return value from scanf (it will give you the number of matching inputs). If you don't get the number you are expecting then you know that something is wrong with the input. In your case it should be 1.

if( scanf("%[0987654321.-]s", buf) == 1 )
{
      if( sanit_check( buf ) ) 
      {
      /* good input */
      }
      else
      {
      /* invalid input */
      }
}
else 
{
/* invalid input */
}

Beyond that you will need to do you own sanity checks on the conversion that you asked scanf to do.

hhafez
I'd prefer the sequence: if (scanf() != 1) { error 1; } else if (!sanity_check()) { error 2: } else { use the good stuff }. It avoids diving off the right-hand side of the page, especially if there's another value to read after this one.
Jonathan Leffler
A: 

"buf" needs to be a pointer. It looks like you are passing by value.

prestomation
It is an array, so "buf" is a pointer to the first element.
Ed Swangren
+12  A: 

Using scanf() is always a bad idea since failure leaves the FILE pointer at an unknown position.

I would suggest using fgets() to get a line in, followed by sscanf() on the string to actually check and process it.

This also allows you to check the string for those characters you desire (either via a loop or with a regular expression), something which the scanf family of functions is not really suited for.

Using scanf() with a "%d" or "%f" will stop at the first non-number character so won't catch trailing errors like "127hello", which will just give you 127.

If you really must use the [] format specifier, I don't think it's meant to be followed by s.

paxdiablo
+1: Good suggestion to use sscanf over scanf. Another problem with scanf (and sscanf) is that numbers that don't fit into the range of the conversion types cause undefined behavior and no way to check for error, it is usually better to use fgets and strtol/strtod.
Robert Gamble
And if you do use the [] conversion specifier, *please* include a maximum field width to avoid buffer overflows!
Robert Gamble
Scanf is a simple incarnation of evil. fscanf is worse.Pax and Robert have it right. Listen carefully to them.
EvilTeach
+1  A: 

You seem to want to validate a string as input. It depends on whether you want to validate that your string contains a double or a int. The following checks for a double (leading and trailing whitespace is allowed).

bool is_double(char const* s) {
    int n;
    double d;
    return sscanf(s, "%lf %n", &d, &n) == 1 && !s[n];
}

sscanf will return the items converted (without '%n'). n will be set to the amount of the processed input characters. If all input was processed, s[n] will return the terminating 0 character. The space between the two format specifiers accounts for optional trailing whitespace.

The following checks for an int, same techniques used:

bool is_int(char const* s) {
    int n;
    int i;
    return sscanf(s, "%d %n", &i, &n) == 1 && !s[n];
}

There was a question on that here, which include also more C++'ish ways for doing this, like using string streams and functions from boost, like lexical_cast and so on. They should generally be preferred over functions like scanf, since it's very easy to forget to pass some '%' to scanf, or some address. scanf won't recognize that, but instead will do arbitrary things, while lexical_cast, for instance, will throw an exception if anything isn't right .

Johannes Schaub - litb
+1  A: 

My approach would be to read user input into string and then convert it to long using strtol(). strtol() returns pointer to the first character in input string it failed to process so by checking this character you'd know whether complete string was parsed, like this:

char *string;
char *endptr;
long result;

scanf("%as", string);
errno = 0;
result = strtol(string, &endptr, 10);
if (ERANGE == errno)
    printf("Input number doesn't fit into long\n");
else if (*endptr)
    printf("%s is not a valid long number - it contains invalid char %c\n",
        string, *endptr);
else
    printf("Got %ld\n", result);

In this snippet scanf() is instructed to automatically allocate string big enough using format modifier "a" (this is GNU extension). If you are not on GNU, allocate string manually and limit the max size in scanf() format.

This approach allows better error handling.

qrdl