Parsing elements from a string

M

millerm

I'm obviously new to C, and have been trying different things to get
this done, but I'm at the end of the line and need some suggestions.

I am reading a string in from a user, in the form of a name, score,
and number of questions. It will be something like: Mark 98.2 20
all separated by spaces. I want to move each element into a temporary
variable so I can work with it, but thats where I'm running into
trouble. In a perfect world I would first move "Mark" to a variable,
work with it, then move "98.2", etc...

Any pointers or tips to methods, pages I might read, etc would be
appreciated. Thanks very much all.

Mark Miller
 
F

Frane Roje

I think sscanf() is what you are looking for.
Maybe something like this.
float score;

int numq;

char name[21];

char line[81];

gets(line);

sscanf(line,"%s %f %d",&name,&score,&numq);



HTH


--
Frane Roje

Have a nice day

Remove (*dele*te) from email to reply
 
E

Eric Sosman

I'm obviously new to C, and have been trying different things to get
this done, but I'm at the end of the line and need some suggestions.

I am reading a string in from a user, in the form of a name, score,
and number of questions. It will be something like: Mark 98.2 20
all separated by spaces. I want to move each element into a temporary
variable so I can work with it, but thats where I'm running into
trouble. In a perfect world I would first move "Mark" to a variable,
work with it, then move "98.2", etc...

Any pointers or tips to methods, pages I might read, etc would be
appreciated. Thanks very much all.

Mark Miller

char string[] = "Mark 98.2 20";
char name[20+1];
double score;
int questions;
if (sscanf(string, "%20s %lf %d", name, &score, &questions) == 3) {
printf ("The name is %s\n", name);
printf ("The score is %f\n", score);
printf ("There are %d questions\n", questions);
}
else {
printf ("The input is indecipherable\n");
}

There are two important points to note here. First, you not
only apply sscanf() to the input string to pick apart the pieces,
but you also check that sscanf() actually found all three. This
defends against user input like "Mark Spitz 28.9 XX".

The second point has to do with the use of "%20s" instead
of the all-too-easy "%s". The problem with the latter is that
it will accept as much input as the user cares to type, even
if it's much too long for the name[] array:

"RumpelstiltskinTheMighty 98.2 20"

could cause considerable grief without such a check. It's not
altogether perfect, because the irascible dwarf might be upset
by having his name rendered as "RumpelstiltskinTheMi" and put a
curse on you ... Still, it's better than storing the five
characters "ghty" (do you see all five?) into non-existent places
and having completely unpredictable things happen.

sscanf() can be made to do more elaborate tricks, and there
are other approaches that don't use sscanf() at all -- but with
what I judge to be your current grasp of C, it might be better
to leave those for another day.
 
M

Malcolm

"Frane Roje"
char name[21];
char line[81];

gets(line);
If you're going to use gets() then make the buffer at least 1000. This still
won't protect you from malicious overflows. Chuck Falconer's ggets() is a
drop in replacement.
sscanf(line,"%s %f %d",&name,&score,&numq);
Here you have the same problem, a name of over 20 characters will corrupt
memory. However you can get round this by adding a width specifier.
You also need to check the return value to make sure all fields were
converted.
 
F

Frane Roje

I just wanted to give an idea, so I appologize for any
mistakes in my solution 'cause it's intention was to give
an idea not a complete solution.

--
Frane Roje

Have a nice day

Remove (*dele*te) from email to reply
 
C

CBFalconer

Eric said:
I am reading a string in from a user, in the form of a name,
score, and number of questions. It will be something like:
Mark 98.2 20 all separated by spaces. I want to move each
element into a temporary variable so I can work with it, but
thats where I'm running into trouble. In a perfect world I
would first move "Mark" to a variable, work with it, then
move "98.2", etc...

Any pointers or tips to methods, pages I might read, etc
would be appreciated. Thanks very much all.

char string[] = "Mark 98.2 20";
char name[20+1];
double score;
int questions;
if (sscanf(string, "%20s %lf %d", name, &score, &questions) == 3) {
printf ("The name is %s\n", name);
printf ("The score is %f\n", score);
printf ("There are %d questions\n", questions);
}
else {
printf ("The input is indecipherable\n");
}

There are two important points to note here. First, you not
only apply sscanf() to the input string to pick apart the pieces,
but you also check that sscanf() actually found all three. This
defends against user input like "Mark Spitz 28.9 XX".

The second point has to do with the use of "%20s" instead
of the all-too-easy "%s". The problem with the latter is that
it will accept as much input as the user cares to type, even
if it's much too long for the name[] array:

"RumpelstiltskinTheMighty 98.2 20"

could cause considerable grief without such a check. It's not
altogether perfect, because the irascible dwarf might be upset
by having his name rendered as "RumpelstiltskinTheMi" and put a
curse on you ... Still, it's better than storing the five
characters "ghty" (do you see all five?) into non-existent places
and having completely unpredictable things happen.

sscanf() can be made to do more elaborate tricks, and there
are other approaches that don't use sscanf() at all -- but with
what I judge to be your current grasp of C, it might be better
to leave those for another day.

Considering the OP's experience, your solution is probably the
most practical, but omits the problem of getting the line in the
first place, and how to recover from an error. The OP whould
under NO circumstances use gets(), regardless of recommendations
elsethread. Similarly, he should not be tempted to replace sscanf
with scanf, because error recovery will become a bear.

Even if sscanf succeeds in inputting various fields, there are
probably range and other validity checks to be applied. The
interactive programmers work is never done :)
 
R

Richard Bos

[ Please do not top-post. ]
I think sscanf() is what you are looking for.
Maybe something like this.

Possibly sscanf() is what he wants, but this _certainly_ isn't it. You
are asking for trouble, the way you've done it.
float score;
int numq;
char name[21];
char line[81];

gets(line);

Never, ever, _ever_ use gets(). Not even for a throwaway, one-shot
program. Ever. It cannot be used safely. Yes, people may say "but I know
how I'm running my program, and I know that I'll never enter more than
80 characters, and I know that...". People like that are going to get
themselves electrocuted one day, because they know there isn't any
current on that pair of wires...
Besides, the only, very vague, advantage gets() may have over fgets() in
some eyes, the fact that you don't have to worry about any newline at
the end of the buffer, is negated by the use of sscanf(), anyway.

Use this instead:

fgets(line, sizeof line, stdin);
sscanf(line,"%s %f %d",&name,&score,&numq);

First of all, sscanf() using %s without any size limit is nearly as
dangerous as gets(). Nearly, because if you know that the string you
copy into is as large as the input buffer, you also know that you cannot
possibly overrun the output... but that isn't the case, here. What if
someone enters "Featheringstonehaughe 14.4 27"?
Moreover, %s only reads a single word. "Joannes Paulus II 3.0 -4" would
cause problems. Granted, this one is less simple to overcome; you'd need
to find the first float, or the last two tokens, and then read
everything up to that.
Also, &name is a pointer to an array of char, not a pointer to a char.
All you need is name; or if you think it's clearer, &name[0].

Another problem is that you do not check how many fields you read
succesfully. This may not seem a problem if you assume that all input is
perfect, but wait until someone enters "Jack", enter, "18.4", enter,
"12", enter, and see what happens if you try to use the resulting
scores. sscanf() returns a value; read it, and complain if it isn't what
you expected it to be.

Richard
 
A

August Derleth

gets(line);

Fools and those who have bought bad books use gets(). Everyone else uses
fgets() or the sadly nonstandard ggets().

No, I don't care /why/ you posted that code. I don't care how informal
it's supposed to be. It's crap, and it will only give students of the
language the wrong idea.
 

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

Members online

No members online now.

Forum statistics

Threads
474,141
Messages
2,570,818
Members
47,367
Latest member
mahdiharooniir

Latest Threads

Top