Determine if sscanf Read Anything with %*s

656 views Asked by At

Say that I have the string:

char* foo = "  blah  blee  bleck  ";

Now say that I want to read and throw away the first two words:

int bar = 0;

sscanf(foo, "%*s%n", &bar);
foo += bar;

if(bar > 0) sscanf(foo, "%*s%n, &bar);

My question is how can I tell if the second sscanf read anything?

Do I need to 0 out bar in between each read to determine if a string was actually read, or is there a better way?

EDIT:

Checking the sscanf return value will not work because %*s and %n do not increase sscanf's return value:

printf("%d ", sscanf(foo, "%*s%n", &bar));
printf("%d ", bar);
printf("%d ", sscanf(foo + bar, "%*s%n", &bar));
printf("%d\n", bar);

Will output:

0 6 0 6

5

There are 5 answers

1
UserM On BEST ANSWER

Check the return value of sscanf call, as the document ion states an integer value would be returned.

int sscanf( const char          *buffer, const char          *format, ... ); (C99)

I tried out a small piece of code in my machine similar to your query, and indeed sscanf returns negative values for unsuccessful operation.

Test code:

#include <stdio.h>

void main(void)
{
char* foo = "  blah  bleee  bleckk  ";
//char* foo = "  blah";
int bar = 0;
int ret = -10;

printf("#m1 foo:%s\n",foo);
printf("#m1 bar:%d\n",bar);
printf("#m1 ret:%d\n\n",ret);

ret = sscanf(foo, "%*s%n", &bar);
foo += bar;
printf("#m2 foo:%s\n",foo);
printf("#m2 bar:%d\n",bar);
printf("#m2 ret:%d\n\n",ret);

if(bar > 0)
{
    ret = -10;
    ret = sscanf(foo, "%*s%n", &bar);
    foo += bar;
}
printf("#m3 foo:%s\n",foo);
printf("#m3 bar:%d\n",bar);
printf("#m3 ret:%d\n",ret);
}

Test output for two scenarios:

./a.out
#m1 foo:  blah  bleee  bleckk
#m1 bar:0
#m1 ret:-10

#m2 foo:  bleee  bleckk
#m2 bar:6
#m2 ret:0

#m3 foo:  bleckk
#m3 bar:7
#m3 ret:0


./a.out
#m1 foo:  blah
#m1 bar:0
#m1 ret:-10

#m2 foo:
#m2 bar:6
#m2 ret:0

#m3 foo:oo:%s

#m3 bar:6
#m3 ret:-1

The issue is the bar value is not set to zero for failed sscanf calls.

3
chux - Reinstate Monica On

... to read and throw away the first two words:

const char* foo = "  blah  blee  bleck  ";
int bar = 0;
sscanf(foo, "%*s%*s% %n", &bar);
if (bar) {
  printf("Success: string with first two words thrown away:'%s'\n", &foo[bar]);
}

The above added a space before "%n" to throw away white-space after "blee".


To tell if the second sscanf read anything? Do I need to 0?

Yes, resetting bar = 0; is an easy way. "%n" is not acted on unless the preceding "%*s" scanned some non-white-space.

char* foo = "  blah  blee  bleck  ";
int bar = 0;
sscanf(foo, "%*s%n", &bar);
foo += bar;

if(bar > 0) {
  puts("first sscanf read something.");
  bar = 0;
  sscanf(foo, "%*s%n", &bar);
  if (bar) puts("second sscanf read something.");
}
0
hdante On

If you're ignoring whitespace, the proper way is to test for EOF in sscanf() return value. EOF means that %*s didn't read anything. If you're not ignoring whitespace, the solution is trivial: if *foo != 0 before the sscanf, then sscanf will read something.

2
unwind On

The better way would probably be to use some proper tokenization code, and not build upon sscanf(). I would probably write something like this:

const char * skip_word(const char *s)
{
  while(isspace(*s))
   ++s;
  while(*s && !isspace(*s))
   ++s;
  return s;
}

That can be used to skip leading whitespace followed by a word, where a word is very loosely defined as "non-whitespace characters". You can then extract the third word by calling the above twice, of course checking the intermediate return values forn sensibility. Something like that.

But yes, you would have to clear bar to get a fresh interpretation, of course.

The spec seems somewhat confused about whether %n is included in the return value, that alone tells me it's not the best approach.

6
GreenScape On

From sscanf() documentation:

RETURN VALUE

These functions return the number of input items successfully matched and assigned, which can be fewer than provided for, or even zero in the event of an early matching failure.

So, if sscanf() does not return the number of matched you expect it probably didn't read what you wanted.