ARW
ARW

Reputation: 3416

Ignoring integers that are next to characters using sscanf()

Sorry for the simple question, but I'm trying to find an elegant way to avoid my program seeing input like "14asdf" and accepting it just as 14.

if (sscanf(sInput, "%d", &iAssignmentMarks[0]) != 0)

Is there an easy way to prevent sscanf from pulling integers out of mangled strings like that?

Upvotes: 6

Views: 1016

Answers (5)

Jonathan Leffler
Jonathan Leffler

Reputation: 753940

You can't directly stop sscanf() from doing what it is designed and specified to do. However, you can use a little-known and seldom-used feature of sscanf() to make it easy to find out that there was a problem:

int i;

if (sscanf(sInput, "%d%n", &iAssignmentMarks[0], &i) != 1)
    ...failed to recognize an integer...
else if (!isspace(sInput[i]) && sInput[i] != '\0')
    ...character after integer was not a space character (including newline) or EOS...

The %n directive reports on the number of characters consumed up to that point, and does not count as a conversion (so there is only one conversion in that format). The %n is standard in sscanf() since C89.

For extracting a single integer, you could also use strtol() - carefully (detecting error conditions with it is surprisingly hard, but it is better than sscanf() which won't report or detect overflows). However, this technique can be used multiple times in a single format, which is often more convenient.

Upvotes: 3

StilesCrisis
StilesCrisis

Reputation: 16290

This is easy. No fancy C++ required! Just do:

char unusedChar;
if (sscanf(sInput, "%d%c", &iAssignmentMarks[0], &unusedChar) == 1)

Upvotes: 2

Serge Wautier
Serge Wautier

Reputation: 21878

You want to read integers from strings. It is easier to do this with strtol instead of sscanf. strtol will return, indirectly via endptr, the address just after the last character that was succesfully read into the number. If, and only if, the string was a number, then endptr will point to the end of your number string, i.e. *endptr == \0.

char *endptr = NULL;
long n = strtol(sInput, &endptr, 10);
bool isNumber = endptr!=NULL && *endptr==0 && errno==0;

(Initial whitespace is ignored. See a strtol man page for details.

Upvotes: 2

Ian
Ian

Reputation: 4457

If you can use c++ specific capabilities, there are more clear ways to test input strings using streams.

Check here: http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.2

If you're wondering, yes this did come from another stack overflow post. Which answers this question: Other answer

Upvotes: 0

John Bode
John Bode

Reputation: 123468

scanf isn't that smart. You'll have to read the input as text and use strtol to convert it. One of the arguments to strtol is a char * that will point to the first character that isn't converted; if that character isn't whitespace or 0, then the input string wasn't a valid integer:

char input[SIZE]; // where SIZE is large enough for the expected values plus
                  // a sign, newline character, and 0 terminator
...
if (fgets(input, sizeof input, stdin))
{
  char *chk;
  long val = strtol(input, &chk, 10);
  if (*chk == NULL || !isspace(*chk) && *chk != 0)
  {
    // input wasn't an integer string
  }
}

Upvotes: 0

Related Questions