Help with sscanf() needed desperately

A

Artemio

Dear folks,

I need some help with using the sscanf() function. I need to parse a
string which has several parameters given in a "A=... B=... C=..." way,
and each has a different type (one is a text string, another is a
decimal, next one is float, etc.).

I have GCC 4.0.1 on Mac OS X Tiger.

Here is an example of what I am trying to do.

<code>

char A[16];
int B;
float C;

sscanf( "A=Test B=25 C=3.14159", "A=%s B=%d C=%f", A, &B, &C);

printf("%s %d %f",A,B,C);

</code>

This prints "Test 0 0.0000"

I also tried this:

<code>
....
sscanf( "A=Test B=25 C=3.14159", "A=%s",A);
sscanf( "A=Test B=25 C=3.14159", "B=%d",&B);
sscanf( "A=Test B=25 C=3.14159", "C=%f",&C);
....
</code>

but this gives same results...

What am I doing wrong? :-/


Thanks for any help.

Artemiy.
 
S

Szabolcs Borsanyi

Dear folks,

I need some help with using the sscanf() function. I need to parse a
string which has several parameters given in a "A=... B=... C=..." way,
and each has a different type (one is a text string, another is a
decimal, next one is float, etc.).

I have GCC 4.0.1 on Mac OS X Tiger.

Here is an example of what I am trying to do.

<code>

char A[16];
int B;
float C;

sscanf( "A=Test B=25 C=3.14159", "A=%s B=%d C=%f", A, &B, &C);
printf("%s %d %f",A,B,C);
</code>
This prints "Test 0 0.0000"

Surrounding it with a main() {...} and adding the header include, I failed
to reproduce your problem (probably on the same system as yours).
I also tried this:

<code>
...
sscanf( "A=Test B=25 C=3.14159", "A=%s",A);
sscanf( "A=Test B=25 C=3.14159", "B=%d",&B);
sscanf( "A=Test B=25 C=3.14159", "C=%f",&C);
...
</code>

This one con better understand: sscanf does not keep the position information
within the string between subsequent calls. It always starts form the beginning
"A=...", so there is no match in the second and third line.
In case of a stream (file), it is a completely different story, then you
use fscanf and you will find the expected behaviour.

By the way: you could check the return value of sscanf (if you are interested
in handling erroneous cases).

If you split your string into a white separated list, or you get it already
so (e.g. from argv, the 2nd argument of main()), you can use the macros

#define dopt(name) sscanf(*argv,#name " = %lf",&name)
#define iopt(name) sscanf(*argv,#name " = %d",&name)
#define sopt(name,size) sscanf(*argv,#name " = %" #size "s",name)

I have in my parsing routine a line
while(*++argv) sopt(A,16) || iopt(B) || dopt(C) || fprintf(stderr,"No!\n");

Consider limiting the size of the read string as you see in the third macro.

Hmm, did it help anything?

Szabolcs Borsanyi
 
M

Michael Mair

Artemio said:
Here is an example of what I am trying to do.

<code>

char A[16];
int B;
float C;

sscanf( "A=Test B=25 C=3.14159", "A=%s B=%d C=%f", A, &B, &C);

printf("%s %d %f",A,B,C);

</code>

It is most of the time a good idea to copy&paste an actual
compiling minimal example showing your problem. With the following,
I obtain the desired results:

#include <stdio.h>

int main (void)
{
char A[16];
int B;
float C;

if (3 == sscanf("A=Test B=25 C=3.14159",
"A=%15s B=%d C=%f",
A, &B, &C)
)
{
printf("%s %d %f\n",A,B,C);
}

return 0;
}

Note
a) the inclusion of <stdio.h> to have prototypes of
printf() and sscanf() in scope
b) checking the return value of sscanf() to make sure that
all conversions were successful
c) the '\n' as last output character to make sure that the
output actually happens (without, this is not guaranteed).
This prints "Test 0 0.0000"

My version using your printf() and a slightly altered sscanf()
call prints
Test 25 3.141590
I also tried this:

<code>
...
sscanf( "A=Test B=25 C=3.14159", "A=%s",A);
sscanf( "A=Test B=25 C=3.14159", "B=%d",&B);
sscanf( "A=Test B=25 C=3.14159", "C=%f",&C);
...
</code>

This is wrong.
What am I doing wrong? :-/

I cannot tell -- you did not give us said complete programme.


Cheers
Michael
 
A

Artemio

Hello guys, and thanks so much for the replies!

Here come my questions:

1. I am trying to parse a line this way:

char WaveType[2];
float B0, B1;
int Angle, Repeat;

sscanf( Line, "W=%s a=%d R=%d B0=%f B1=%e", WaveType, &Angle, &Repeat,
&B0, &B1);

The "Line" string contains:

W=TE a=45 R=3 B0=1.0 B1=1e-9

Now the WEIRD thing: when I do it like above it WORKS, but if I read
this string from a file line-by-line, and one of the lines is like
this, it DOESN'T work! Only the WaveType variable gets it's data
properly.

E.g.

while( fgets( Line, sizeof Line, File ) != NULL ){
sscanf( Line, "W=%s a=%d R=%d B0=%f B1=%e", WaveType, &Angle,
&Repeat, &B0, &B1);
}

:-/

2. What is the best way to read this line from a file and parse it into
variables defined above, but in a random order? E.g. sometimes the
string may be W=TE a=45 R=3 B0=1.0 and next time it'll look like W=TE
R=3 B0=1.0 a=45.


Thanks again,

Artemiy.
 
A

Artemio

Hey guys, sorry, reading with sscanf( Line, "W=%s a=%d R=%d B0=%f
B1=%e", WaveType, &Angle, &Repeat, &B0, &B1) works fine, it appears the
data was not printed in a correct way, that's all.

Anyway, the important thing now, what is the best way to parse the Line
string if the order of these X=... variables can change?

Thanks again,

Artemiy.
 
C

Chris Torek

[In general, a] "Line" string contains [something like]:
W=TE a=45 R=3 B0=1.0 B1=1e-9
[which can be read relatively easily with sscanf().]
2. What is the best way to read this line from a file and parse it into
variables defined above, but in a random order? E.g. sometimes the
string may be W=TE a=45 R=3 B0=1.0 and next time it'll look like W=TE
R=3 B0=1.0 a=45.

Your best bet is probably to write your own "line parser". You
have a number of decisions to make:

- What happens if a line is malformed?

W==1=1=1=47= R:hello, B0=1.2.3.4

- What happens if a line is missing one or more values?

W=TE B1=1.0 R=3 # but no value for "a" or "B0"

If all entries are of the form <text1>=<text2>, where text1 and
text2 never contain any whitespace, you can easily break up a line
into whitespace-separated groups, then break up the broken-up parts
into "="-separated pieces, then look up the <text1> part to see
whether it is "W", "a", "R", "B0", or "B1". The result of that
will determine how the <text2> part is to be treated (as a "word"
for W, as an integer for "R" and "a", and as a floating-point value
for B0 and B1).

Although the strtok() function is quite ugly, it happens to be
fairly well suited for the first task. The second can be done
with a simple "strchr" (do not use strtok() inside the outer
loop!):

#include <string.h>
...
#define WHITESPACE " \t\n" /* add \b \r \f \v if desired */
...

/* handle one line of input */
char *entry, *value;

for (entry = strtok(line, WHITESPACE); entry != NULL;
entry = strtok(NULL, WHITESPACE)) {
value = strchr(entry, '=');
if (value == NULL)
... do something about malformed entry ...

/* wipe out "=" and leave "value" pointing to the value */
*value++ = 0;

if (strcmp(entry, "W") == 0)
... handle W value ...
else if (strcmp(entry, "a") == 0)
... handle "a" value ...
else if (strcmp(entry, "R") == 0)
... handle R value ...
else if (strcmp(entry, "B0") == 0)
... handle B0 value ...
else if (strcmp(entry, "B1") == 0)
... handle B1 value ...
else
... do something about unknown variable ...
}
... check to make sure all 5 values were specified, if needed ...
 
A

Artemio

Wow Chris, thank you *so* much for the suggestions! I learned strtok()
a bit but never managed to write anything working with it. Thanks a lot
again, I will learn your code.
 
S

Szabolcs Borsanyi

Artemio said:
Here come my questions:
1. I am trying to parse a line this way:

char WaveType[2];
float B0, B1;
int Angle, Repeat;

sscanf( Line, "W=%s a=%d R=%d B0=%f B1=%e", WaveType, &Angle, &Repeat,
&B0, &B1);

The "Line" string contains:

W=TE a=45 R=3 B0=1.0 B1=1e-9


Hmm, you are filling a 3-char-long string into a two-char-long array.
If you are lucky, the final NUL written after WaveType does not affect
other variables (alignment holes), but this is still an undefined behaviour.
while( fgets( Line, sizeof Line, File ) != NULL ){
sscanf( Line, "W=%s a=%d R=%d B0=%f B1=%e", WaveType, &Angle,
&Repeat, &B0, &B1);
}

Well, that's it. You are reading from a file. So my previous posting in the
thread can work in arbitrary order. (But you don't know if an entry is
in the same line or in then next one.)

After you tokenized with strtok or simply with

while( fgets( Line, sizeof Line, File ) != NULL ){
char tokens[8][9]; /* suppose you have at most 8 fields, rest ignored */
int i,s;
/* give default values to your parameters */
s=sscaf(line,"%9s %9s %9s %9s %9s %9s %9s %9s", /* type a lot */
tokens[0],tokens[1],.... );
/* no space between '=' and name and value*/
for(i=0;i<s;i++) {
/* here come my dopt,iopt, sopt macros */
/* read the parameters in the order they appear in the file */
}
}

If you know that you do not accept more than say 8 fields, each 8 char long,
this is (perhaps) the simplest way for random field order.

Szabolcs Borsanyi
 
D

Default User

Artemio said:
Wow Chris, thank you so much for the suggestions! I learned strtok()
a bit but never managed to write anything working with it. Thanks a
lot again, I will learn your code.


Please quote enough of the previous message for context. Google does
that automatically now. Your replies belong following or interspersed
with properly trimmed quotes. See the vast majority of posts here for
examples.




Brian
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top