8

我有一个函数,它会进行一系列调用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(). 有没有办法解决这个问题,或者我是否也可以在每次通话时手动处理簿记?

4

3 回答 3

4

你是对的 - 你不能把额外的参数塞进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);})
于 2010-03-17T01:08:28.970 回答
1

没有弄清楚可变参数列表是如何在幕后工作的(从而使您的代码不可移植),就无法修改参数。

但我确实有一个想法,可能会也可能不会。我没有测试过它,因为我真的不认为你应该使用它,但是,如果你一心想要这样做,它可能会有所帮助。

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

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

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

计算%格式字符串中不紧跟%or的字符数*- 换句话说,需要提供给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;
}

如果你决定试一试,我很想听听它的进展情况。这是我的工作(和责任)完成的。我很高兴把电锯卖给你,但是,如果你在使用它的时候把腿砍断了,那就是你的问题了 :-)

于 2010-03-17T00:21:03.593 回答
0

您错误地调用了该函数,请查看char **str暗示引用调用参数的参数:

if(newSscanf(&str, "%d", &fooInt) != 1)
{
   // 错误处理
}
于 2010-03-17T00:21:42.450 回答