Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 598
  • Last Modified:

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


0
elito
Asked:
elito
  • 3
  • 2
  • 2
  • +3
1 Solution
 
sunnycoderCommented:
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) )
    temp1++;
temp=temp1;

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

Cheers!
Sunny:o)
0
 
Kent OlsenData Warehouse Architect / DBACommented:

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
0
 
jayesh_j_patelCommented:
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
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

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

0
 
Kent OlsenData Warehouse Architect / DBACommented:
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
0
 
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
0
 
PaulCaswellCommented:
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

0
 
Kent OlsenData Warehouse Architect / DBACommented:
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
0
 
guynumber5764Commented:
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.

0
 
sunnycoderCommented:
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
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 3
  • 2
  • 2
  • +3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now