1
scanf("%f", &num);

I have to check if thats a valid float variable and stop executing if it has symbols like @ or ! or % etc..

Suggestions?

Maroun
  • 94,125
  • 30
  • 188
  • 241
Yogeesh Seralathan
  • 1,396
  • 4
  • 15
  • 22
  • 9
    The [man page](http://linux.die.net/man/3/scanf) for `scanf` explains that its return value indicates the number of values it matched. In your case, this would be 1 if `num` has been set to a valid `float` – simonc Aug 13 '13 at 13:29
  • 3
    There is no need to write "In C" in your title, the tag already did that. – Maroun Aug 13 '13 at 13:29
  • 2
    Are you looking for a valid float on a line by itself? Because `scanf` will succeed in retrieving a float if it finds one but even if there's "stuff" after it. For example, `scanf("%f", &num)` will return `1` (number of floats read) from an input such as `3.2@#$$%`, putting a value of `3.2` into `num`. It just stops reading when it hits the `@`. If you need to scan past the "stuff" to get to the next float, you'll need a mechanism for that since `scanf("%f"...)` will just sit there, stuck at the `@` on subsequent calls, returning `0`, unless you do something else. – lurker Aug 13 '13 at 13:40
  • You should give us examples of what inputs will be accepted and rejected, if any. Or generally what you want your program to do given a certain bunch of inputs. (Does "stop executing" mean your program should exit with an error message? Or that `scanf()` should stop scanning?) Neither `scanf()` nor `strtol()` do "validation", that is they will accept any string that starts with a float. – millimoose Aug 13 '13 at 13:46

6 Answers6

3

Check the return value from scanf(). It will return the number of successfully scanned inputs - in your case it should be 1. If it is not equal to one, it could be -1 which means "end of file" or 0, which means "input wasn't valid".

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
3

scanf is not your friend, here. Even if you check the result to ensure that you read one whole argument successfully, that doesn't really guarantee that the input is valid in a meaningful sense.

Given input like:

1!!!1!oneone!

the %f parsing will stop (but succeed) after reading the first 1, and leave the read pointer at the following !. This may not be what you consider "valid input".

In that case, try:

if (scanf("%f%c", &f, &c) == 2 && isspace(c))
    /* success */

This will, however, accept things like:

1 oneoneonespace!bang

If anything on the same line is to be considered garbage, then it gets difficult, because scanf doesn't distinguish between spaces and newlines. Perhaps try something like this:

char buffer[1024];
if (fgets(buffer, sizeof(buffer), stdin) != NULL)
{
    if (sscanf(buffer, "%f %c", &f, &c) == 1)
        /* success */
}
sh1
  • 4,324
  • 17
  • 30
2

Using the %f specifier in scanf won't scan anything else, if no float is specified, nothing will be read. You should check the return value of scanf.

There are a few defined return values:

EOF = -1
invalid input = 0
valid = >0
phyrrus9
  • 1,441
  • 11
  • 26
1

Read your input as text, then convert it with the strtod library function; that allows you to check if the input contains any invalid characters:

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
...
char buf=[81];
double result = 0.0;
...
if ( fgets( buf, sizeof buf, stdin ) != NULL )
{
  char *chk;
  double tmp = strtod( buf, &chk );
  if ( isspace( *chk ) || *chk == 0 )
    result = tmp;
  else
    fprintf( stderr, "%s is not a valid floating-point number\n", buf );
}

After the call to strtod, the variable chk will point to the first character in the string that is not part of a valid floating-point constant. If this character is whitespace or 0, then you're good; otherwise you have bad input.

The advantage to this method over scanf is that if your input starts with just one valid character (such as "1blkjhsdf"), it will convert and assign the "1" and return a 1, indicating a successful conversion, even though you'd probably want to reject that input. It also consumes the entire input string (provided the input buffer is large enough), so it doesn't leave any garbage in the input stream to foul up the next read.

scanf is a good tool to use when you know your input is well-formed; otherwise, better to use the method above.

John Bode
  • 119,563
  • 19
  • 122
  • 198
0

You cannot check for both of your conditions in scanf. I would suggest reading whole input into a string, look for special characters. Once this test passes, try re-processing it into a string via sscanf. To search for special characters there is a strpbrk defined in string.h

char* specials = "%@!";
char* buffer[SOME_SIZE];
double value;
int err;

scanf("%s", buffer);
if( strpbrk(buffer, specials) ) return SPECIAL_FOUND;
err = sscanf(buffer, "%f", &value);
if(err <= 0) return NOT_VALID_INPUT; //NOTE: there should probably be not EOF case

// process your value

Note: I did not check the code it's a rough idea to play with.

luk32
  • 15,812
  • 38
  • 62
  • `strtof()` is better still as it tells where it stopped parsing and you can use it to check for `\0`. – trojanfoe Aug 13 '13 at 14:08
  • @trojanfoe I'll yoink that fact for the ideone sample I gave in lulyon's answer, I was using strlen() originally. – millimoose Aug 13 '13 at 14:23
0

You can check the return value from scanf(). If you look at this page :

Return value

On success, the function returns the number of items of the argument list successfully filled. This count can match the expected number of items or be less (even zero) due to a matching failure, a reading error, or the reach of the end-of-file.

So something like :

if ( scanf( "%f", &num ) <= 0 )
{
    // Stop the execution
}
// Continue

will do what you want.

Community
  • 1
  • 1
Pierre Fourgeaud
  • 14,290
  • 1
  • 38
  • 62