• C

Help with scanf


I'm using scanf to parse simple char strings. For example given the string char s[z] = "AB 4.1 6.2", I will use something as this:

char s[MAX];
float fVal1 = 1.0f, fVal2 = 2.0f;

sscanf (sz, "%s %f %f",s, &fVal1, &fVal2);

So now variables s, fVal1 and fVal2 hold the values I need.

But how can I parse a string when the number of arguments is variable? For instance, given the following string:

sz = "ab 2.3 4.5 2 6.7 8.9";

where the number 2 gives the number of parameters that follow it, in this case parameters 6.7 and 8.9

Any help?

Thank you :-)

Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Hi elito,

> sz = "ab 2.3 4.5 2 6.7 8.9";

sscanf (sz, "%s %f %f %d",s, &fVal1, &fVal2, &num);

now number of expected arguments is held in num .... you can repeat sscanf to get two more ints

a more elegant and preferred way is to parse the entire string yourself using strchr() (you can also use strtok)

temp1 = string;
while ( ( temp = strchr(string, ' ')) != NULL )
        /* print string between temp1 and temp */
        temp1 = temp + 1;

the above snippet assumes that all args are separated by a single space ... if there can be multiple spaces, you can always replace temp1=temp+1 by
temp1 = temp;
while ( isspace (*temp1) )

If you are dealing with variable number of arguments in a function (like printf), look into va_arg


Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Kent OlsenDBACommented:

Hi elito,

This can be broken into two problems, or handled as a single problem.  The single problem solution is much cleaner.  Just expand your sscanf() statement so that it can handle the largest parameter list that you'll accept.  Actually, have it accept one MORE that you'll handle.  That way if N is greater than your limit, you know that too many parameters are on the line.

sscanf (string, " %s %f %f %N %f %f %f %f %f %f %f %f %f %f", s, &Val1, %Val2, N, &F1, &F2, &F3, &F4, &F5, &F6, &F7, &F8, &F9, &F10);

'N' will be set to the number of parameters that follow it, F1..Fn will be set the the value of its corresponding parameter, and the rest of the Fx variables will remain unchanged.

Note that the scan string starts with a space.  This tells sscanf() that the line MIGHT start with whitespace and to ignore the spaces if they are present.

Good Luck,
Try this:

char *temp;
char *seperator = " "; // this is a space, also you can add comma, semicolon etc as a seperator
temp = strtok(sz, seperator);
    valFlt = atof(temp); // for float data, for int type use valInt = atoi(temp);
    temp = strtok(NULL, seperator); // make sure to use NULL as a first parameter to strtok function

Still I have a question how do you know what is the data type for each item in the string. You must have some kind of indication or something to know the data type otherwise how you will parse string into the proper data type?
Hope will help
Big Business Goals? Which KPIs Will Help You

The most successful MSPs rely on metrics – known as key performance indicators (KPIs) – for making informed decisions that help their businesses thrive, rather than just survive. This eBook provides an overview of the most important KPIs used by top MSPs.

elitoAuthor Commented:

Thanks all for your comments. I think the easiest wat for me would be to use the sscanf function using a variable number of arguments va_arg. Does anyone know how to use sscanf and va_arg?

Thanks :-)

Kent OlsenDBACommented:
Hi elito,

va_arg won't help you here.  It's a c macro that extracts formal parameters from within a function.

SomeFunction (char *s, float f1, float f2, int N, ....);

This would be the header format if you were building a function in C to handle the parameters.  But you're trying to extract DATA.  Not code a function.

The sscanf() function that I offered solves the entire problem with one line of C code.  Can't get much cleaner code than that.

elitoAuthor Commented:
Thanks Kent,

Yes, I'm sure your code will work fine, the main problem is that the number of variable arguments is undefined. Normally they are about 4-8 but they can be *any*number. I thought that as printf accepts variable arguments through va_arg (if I'm correct, not sure ...), the same could be applied to sscanf

This is not 'tested' code but I'd do something like:

float f [MAX]; // Some maximum number of floats.
int took; // How manty characters the scanf took.
int i, pos; // Which float and where we are in 's'.
char * s = "1.2 1.3 1.5" // For example.

for ( i = 0; pos = 0; i < MAX && pos < strlen(s) && scanf ( &s[pos], "%f %n", &f[i], &took ) == 2; i++, pos += took );

If you can 'consume' each float inside the loop then you could even remove the limit. The essence here is that %n generates the number of charactes taken and the return value of sscanf is the number of correct scans achieved.

NOTE: You may have to use '== 1'. I cant remember if %n is counted in the return result.


Kent OlsenDBACommented:
Hi Elito,

*any* number is a lot.  :)  There has to be an upper limit, even if that limit is "whatever memory will hold".  You've got to have some place to store them, you've got to have a way to reference them, and you will also need a use for them.  If all three of these requirements aren't met, then the data is meaningless and can probably be ignored.

How are you getting the string?  read(), fgets(), etc all have defined limits that are the length of the buffer.  If you know the length of the buffer, you know the upper limit.

sidebar -- I just reread the first two paragraphs and they sound a bit antagonistic.  I don't mean to come off that way.

So... to apologize :) here's a bit of code that should do what you want.

char *Data = "ab 2.3 4.5 2 6.7 8.9";  /*  The data to be parsed  */
char *Ptr;          /*  Pointer to field within Data  */

char *DataPrfx;  /*  Ptr to substring that starts the current string  */
float F1,F2;       /*  First two data items  */

int    Expected;       /*  Number of data items expected following the 'header'  */
int    Count;           /*  Number of data items found following the header  */

float *Table;     /*  Data items that follow the Count field  */

  Ptr = strtok (Data, " ");
  DataPrfx = Ptr;  /*  "ab"  */

  Ptr = strtok (NULL, " ");
  F1 = atof (Ptr);  /*  2.3  */

  Ptr = strtok (NULL, " ");
  F2 = atof (Ptr);  /*  4.5  */

  Ptr = strtok (NULL, " ");
  Expected = atoi (Ptr);

  Table = (float *) malloc (Expected * sizeof (float));
  for (Count = 0; Count < Expected; Count++)
    Ptr = strtok (NULL, " ");
    if (Ptr == NULL)  /*  Not enough data items on the line. */
    Table[Count] = atof (Ptr);

Note that sanity checks should be inserted into the program.  In this example, if the string doesn't contain the first 4 fields, strtok() will return NULL and the results might include core dumps.  :)

I seem to say this a lot but...Sunny's soln looks like best fit for your needs.  Be careful about *any* number of arguments and fgets() though.  Since the line length is (by definition) unlimited but your buffer is fixed length, there may be a vuln there.

Hi Elito,

va_args is used to parse arguments in the function which receives variable number of arguments while being called.
Here you have variables in a string and you need to read them ... Thus you will have to rely on string parsing ...

int i = 0;
char buffer[64];
temp1 = string;
while ( ( temp = strchr(string, ' ')) != NULL )  //till string is exhausted, get location of next space char
       i++;           //increment counter
       strncpy ( buffer, temp1, temp - temp1 );     //
       buffer [temp-temp1] = 0;
       printf ( "argument number %d -- %s", i, buffer );
       temp1 = temp + 1;

this code snippet will parse the string and print out all the arguments irrespective of how many arguments are present ... (you will have to print the last arg after loop
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today

From novice to tech pro — start learning today.