Link to home
Start Free TrialLog in
Avatar of elito
elito

asked on

Help with scanf

Hi,

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 :-)


ASKER CERTIFIED SOLUTION
Avatar of sunnycoder
sunnycoder
Flag of India image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial

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,
Kent
Avatar of jayesh_j_patel
jayesh_j_patel

Try this:

char *temp;
char *seperator = " "; // this is a space, also you can add comma, semicolon etc as a seperator
temp = strtok(sz, seperator);
while(temp)
{
    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
Jayesh
Avatar of elito

ASKER

Hi,

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 :-)

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.


Kent
Avatar of elito

ASKER

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
Elito,

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.

Paul

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. */
      break;
    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.  :)

Kent
Elito,
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