sscanf包装函数在C中前进字符串指针

时间:2010-03-17 00:11:05

标签: c variadic-functions scanf

我有一个函数对sscanf()进行一系列调用,然后在每个函数之后更新字符串指针以指向sscanf()未使用的第一个字符,如下所示:

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

为了清理它并避免重复这几次,我想把它封装成一个很好的实用函数,看起来如下所示:

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;
}

然后我可以简化下面的调用以删除附加参数/簿记:

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

不幸的是,我找不到将&length参数直接附加到arg列表末尾或newSscanf()内的方法。有没有办法解决这个问题,还是我在每次通话时手动处理簿记?

3 个答案:

答案 0 :(得分:3)

你是对的 - 你不能将额外的参数填入va_list。你能做的最好的可能就是这样的一些宏观技巧:

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)

...并要求来电者:

NEW_SSCANF_INIT;

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

如果您可以使用GCC扩展,则可以使用“语句表达式”取消NEW_SSCANF_INIT部分,使其更清晰:

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

答案 1 :(得分:1)

如果没有弄清楚可变参数列表是如何工作的(并因此使代码不可移植),则无法修改参数。

但我确实有一个想法可能会或可能不会奏效。我没有测试过,因为我真的不认为你应该使用它,但是,如果你真的很想做到这一点,它可能有所帮助。

由于您只想获得扫描的字符数,您应该意识到必须与调用者变量的实际设置同时进行。

让您的代码扫描字符串以根据调用者的需要设置参数。根本不需要改变。

下一阶段是稍微棘手的一个。

计算格式字符串中%字符的数量,这些字符不会紧跟%* - 换句话说,需要提供的变量数量sscanf。如果这大于上限,则断言(参见下面的代码)。

然后在格式字符串的末尾添加%n个序列,以确保您获得字符数。

然后,使用新的格式字符串,使用垃圾缓冲区(重复)从扫描中接收所有值,包括最后一个(字符数)。

这样的事情(调试的责任在于你):

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;
}

如果您决定试一试,我会对您的听力感兴趣。那是我的工作(和责任)完成的。我很高兴卖给你电锯但是,如果你在使用它的时候剪掉你的腿,这就是你的问题: - )

答案 2 :(得分:0)

您正在错误地调用该函数,请查看char **str的参数,其中包含一个call-by-reference参数:

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