代码之家  ›  专栏  ›  技术社区  ›  Dusty

SScanf包装函数在C中推进字符串指针

  •  7
  • Dusty  · 技术社区  · 15 年前

    我有一个函数可以对 sscanf() 然后,在每次更新之后,更新字符串指针以指向第一个不被使用的字符 sSCAN() 像这样:

    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 参数直接或在参数列表的末尾 newSscanf() . 有什么办法可以解决这个问题吗?还是每次打电话都要我亲自处理簿记?

    3 回复  |  直到 14 年前
        1
  •  3
  •   caf    14 年前

    你是对的-你不能把额外的参数塞进 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);})
    
        2
  •  1
  •   paxdiablo    15 年前

    除了弄清楚variadic列表如何在封面下工作(从而使代码不可移植)之外,没有办法修改参数。

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

    因为您只想扫描字符数,所以应该意识到 不要 必须与调用方变量的实际设置同时进行。

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

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

    数一数 % 格式字符串中没有紧跟其后的字符 % * -换句话说,需要提供给 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;
    }
    

    如果你决定试一试的话,我会很感兴趣的。这是我的工作(和责任)。我很乐意把链锯卖给你,但如果你在使用的时候把腿锯断了,那就是你的问题了:—)

        3
  •  0
  •   t0mm13b    15 年前

    您错误地调用了函数,请查看 char **str 这意味着引用调用参数:

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