1

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

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288

5 Answers5

2

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.

UserM
  • 190
  • 2
  • 19
  • The answer in this post is in the second scenario, when `foo` is just "blah". Note that `ret` is -1 (`EOF`) in the last output. With this understanding the @hdante [answer](http://stackoverflow.com/a/27359266/2642059) makes sense. – Jonathan Mee Dec 10 '14 at 13:50
1

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.

hdante
  • 7,685
  • 3
  • 31
  • 36
1

... 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.");
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Nice I learned, from this post that: "A single whitespace in the format string validates any quantity of whitespace characters extracted from the stream (including none)." http://www.cplusplus.com/reference/cstdio/scanf/#parameters – Jonathan Mee Dec 08 '14 at 15:02
  • @Jonathan Mee Another valuable note about `" "`, `"\t"`, `"\n"`, etc. is that they never fail. Scanning will always proceed to the next directive. – chux - Reinstate Monica Dec 08 '14 at 15:28
  • Yeah I read that. Man scanf is so powerful, I just wish I knew all these tricks back in college! – Jonathan Mee Dec 08 '14 at 15:44
0

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.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • By "proper tokenization code" you mean that I write my own function reading in one character at a time, throwing out whitespace, and returning when the next character is whitespace or null? – Jonathan Mee Dec 08 '14 at 13:19
  • While I do think tokenization is a valid alternative, it seems to needlessly introduce complexity to what should be a simple solution. [hdante](http://stackoverflow.com/users/1797000/hdante) and [MuruganV-userm](http://stackoverflow.com/users/2854673/muruganv-userm)'s solution prevents reinventing the wheel. – Jonathan Mee Dec 08 '14 at 14:12
-1

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.

GreenScape
  • 7,191
  • 2
  • 34
  • 64
  • 1
    No, the asterisk suppresses this. – unwind Dec 08 '14 at 13:09
  • @GreenScape Neither "%*s" nor "%n" increase the return value from `sscanf`. (I actually tried this approach first.) – Jonathan Mee Dec 08 '14 at 13:13
  • @JonathanMee Could you tell us why this method will not work? If sscanf fails it should return failure values – UserM Dec 08 '14 at 13:33
  • @MuruganV-userm I have edited the question to demonstrate why this won't work. – Jonathan Mee Dec 08 '14 at 13:49
  • @JonathanMee Do check my answer, as output clearly indicates -ve values for sscanf failure – UserM Dec 08 '14 at 13:52
  • @MuruganV-userm Yup I see that in both your answer and [hdante](http://stackoverflow.com/users/1797000/hdante)'s I see nothing about negative returns in [GreenScape](http://stackoverflow.com/users/966376/greenscape)'s answer. – Jonathan Mee Dec 08 '14 at 14:14