Sscanf trim function to point a string pointer to C

I have a function that makes a series of calls to sscanf() , and then after each updates the line pointer, pointing to the first character that sscanf() does not consume as follows:

 if(sscanf(str, "%d%n", &fooInt, &length) != 1) { // error handling } str+=length; 

To clear it and avoid duplicating it several times, I would like to encapsulate it in a nice utility function that looks something like this:

 int newSscanf ( char ** str, const char * format, ...) { int rv; int length; char buf[MAX_LENGTH]; va_list args; strcpy(buf, format); strcat(buf, "%n"); va_start(args, format); rv = vsscanf(*str, buf, args, &length); //Not valid but this is the spirit va_end(args); *str += length; return rv; } 

Then I could simplify the calls as shown below to remove the extra parameter / bookkeeping:

 if(newSscanf(&str, "%d", &fooInt) != 1) { // error handling } 

Unfortunately, I cannot find a way to add the &length parameter to the end of the arg list directly or otherwise inside newSscanf() . Is there a way around this, or am I also good at manual bookkeeping with every call?

+6
c scanf variadic-functions
source share
3 answers

You are right - you cannot enter additional parameters into va_list . The best you can do is probably some macro definitions like this:

 int _newSscanf ( char ** str, int *length, const char * format, ...) { int rv; va_list args; va_start(args, format); rv = vsscanf(*str, format, args); va_end(args); *str += *length; return rv; } #define NEW_SSCANF_INIT int _ss_len #define newSscanf(str, fmt, ...) _newSscanf(str, &_ss_len, fmt "%n", __VA_ARGS__, &_ss_len) 

... and requires calling the caller:

 NEW_SSCANF_INIT; if (newSscanf(&str, "%d", &fooInt) != 1) { // error handling } 

If you can use the GCC extensions, you can use the "operator expressions" to do away with the NEW_SSCANF_INIT part, making it cleaner:

 #define newSscanf(str, fmt, ...) ({int _ss_len; _newSscanf(str, &_ss_len, fmt "%n", __VA_ARGS__, &_ss_len);}) 
+3
source share

It’s impossible to figure out how variable lists work under covers (and therefore turn your code into non-portable), there is no way to change the arguments.

But I had one thought that may or may not work. I have not tested it, since I really don't think you should use it, but if you are damn tuned for it, this may help.

Since you just want to get the number of characters scanned, you should understand that you do not need to do this at the same time as the actual setting of the caller’s variables.

Ask your code to check the line to give arguments as necessary to the caller. No changes are required at all.

The next step is a bit complicated.

Count the number of % characters in the format string, which is not immediately followed by % or * - in other words, the number of variables to be sent to sscanf . Approve if it is more than your upper limit (see Code below).

Then add the %n sequences to the end of the format string to make sure you get the number of characters.

Then, using your new format string, use the spam buffer (repeatedly) to get all values ​​from the scan, including the last one (number of characters).

Something like this (you are responsible for debugging):

 typedef union { char junk[512]; // Be *very* careful with "%s" buffer overflows. int length; } tJunkbuff; int newSscanf (char **str, const char *format, ...) { int rv, length; char buf[MAX_LENGTH]; tJunkBuff junkbuff; va_list args; // Populate variables. va_start (args, format); rv = vsscanf (*str, buf, args); va_end (args); // Get length. // String scanning for % count and assert/error left out. // Only 20 allowed (depends on number of jb.junk variables below (n-1)). strcpy (buf, format); strcat (buf, "%n"); sscanf (*str, buf, jb.junk,jb.junk, jb.junk, jb.junk, jb.junk, jb.junk,jb.junk, jb.junk, jb.junk, jb.junk, jb.junk,jb.junk, jb.junk, jb.junk, jb.junk, jb.junk,jb.junk, jb.junk, jb.junk, jb.junk, jb.junk); // May need to be "&(jb.junk)" ? *str += jb.length; return rv; } 

I would be interested to hear how this happens if you decide to do it. This is my job (and responsibility). I'm glad to sell you a chainsaw, but if you cut off your leg when using it, then your problem :-)

+1
source share

You are calling the function incorrectly, look at the char **str parameter, which implies the call sign parameter:

  if (newSscanf (& str, "% d", & fooInt)! = 1)
 { 
    // error handling
 }
0
source share

All Articles