Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to validate input using scanf

Tags:

c++

c

scanf

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;
like image 854
Raúl Roa Avatar asked Jan 19 '09 01:01

Raúl Roa


2 Answers

Using scanf() is usually a bad idea for user input since failure leaves the FILE pointer at an unknown position. That's because scanf stands for "scan formatted" and there is little more unformatted than user input.

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.

By way of example, 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. And using it with a non-bounded %s is just begging for a buffer overflow.

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

And, for a robust input solution using that advice, see here. Once you have an input line as a string, you can sscanf to your hearts content.

like image 149
paxdiablo Avatar answered Nov 15 '22 04:11

paxdiablo


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 .

like image 27
Johannes Schaub - litb Avatar answered Nov 15 '22 02:11

Johannes Schaub - litb