【问题标题】:How to capture length of sscanf'd string?如何捕获 sscanf'd 字符串的长度?
【发布时间】:2010-12-09 03:49:14
【问题描述】:

我正在解析一个遵循可预测模式的字符串:

  1. 1 个字符
  2. 一个整数(一位或多位数字)
  3. 1 个冒号
  4. 一个字符串,其长度来自#2

例如:

s5:stuff

我可以很容易地看到如何使用 PCRE 或类似方法来解析它,但为了速度,我宁愿坚持使用纯字符串操作。

我知道我需要分两步完成,因为在我知道它的长度之前我无法分配目标字符串。我的问题是优雅地获取所述字符串的 start 的偏移量。一些代码:

unsigned start = 0;
char type = serialized[start++]; // get the type tag
int len = 0;
char* dest = NULL;
char format[20];
//...
switch (type) {
  //...
  case 's':
    // Figure out the length of the target string...
    sscanf(serialized + start, "%d", &len);
    // <code type='graceful'>
    // increment start by the STRING LENGTH of whatever %d was
    // </code>
    // Don't forget to skip over the colon...
    ++start;
    // Build a format string which accounts for length...
    sprintf(format, "%%%ds", len);
    // Finally, grab the target string...
    sscanf(serialized + start, format, string);
    break;
  //...
}

该代码大致取自我所拥有的(由于手头的问题,该代码不完整),但它应该能说明问题。也许我完全采取了错误的方法。 什么是最优雅的方法?解决方案可以是 C 或 C++(如果有足够的响应,我实际上希望看到竞争方法)。

【问题讨论】:

    标签: c++ c string parsing scanf


    【解决方案1】:

    您可以使用 %n 转换说明符,它不消耗任何输入 - 相反,它需要一个 int * 参数,并将输入消耗的字符数写入其中:

    int consumed;
    
    sscanf(serialized + start, "%d%n", &len, &consumed);
    start += consumed;
    

    (但不要忘记检查 sscanf() 返回 > 0!)

    【讨论】:

    • @R:确实,虽然我被引导相信 %n 转换是否计入 sscanf() 的返回值是未指定的,因此仅在以下位置使用它是安全的结束。
    • 你错过了吗? “执行 %n 转换规范不应增加函数执行完成时返回的赋值计数。”
    • @R:Linux 手册页 声称 TC 与此相矛盾。所以“未指定”实际上根本不是正确的术语。但似乎肯定有一些混淆。
    • 顺便说一句,更好的检查方法是预先为整数分配一个负值,然后在sscanf返回后查看它是否仍然为负。
    • @R.:实际上是 TC1 of C90 包含矛盾的信息,在 §7.9.6.2 示例中。所以很明显,如果您使用的是 C99,那将被取代,但 C90 中的情况似乎有点模糊。
    【解决方案2】:

    使用%n 格式说明符将目前读取的字符数写入整数参数。

    【讨论】:

      【解决方案3】:

      这是一个 C++ 解决方案,它可能会更好,并且经过硬编码专门用于处理您的示例输入,但不需要太多修改即可开始工作。

      std::stringstream ss;
      
      char type;
      unsigned length;
      char dummy;
      std::string value;
      
      ss << "s5:Helloxxxxxxxxxxx";
      
      ss >> type;
      ss >> length;
      ss >> dummy;
      ss.width(length);
      ss >> value;
      
      std::cout << value << std::endl;
      

      免责声明:

      我是 C++ 的菜鸟。

      【讨论】:

      • 额外的x 字符只是为了表明它不会提取它们。
      【解决方案4】:

      您可能只使用 atoi 会忽略冒号。

      例如len = atoi(序列化 + 开始);

      atoi 唯一的问题是,如果它返回零,则可能意味着转换失败,或者长度确实为零。所以它并不总是最合适的功能。

      【讨论】:

        【解决方案5】:

        如果你用空格替换你的冒号,scanf 会停在上面,你可以得到 malloc 的大小,然后运行另一个 scanf 来获取字符串的其余部分`

        int main (int argc, const char * argv[]) {
        char foo[20];
        char *test;
        
        scanf("%s",foo); //"hello world"
        printf("foo = %s\n", foo);//prints hello
        //get size
            test = malloc(sizeof(char)* 10);//replace 10 with your string size
            scanf("%s", test);
        printf("test = %s\n", test);//prints world
        
        return 0;
        }
        

        `

        【讨论】:

          【解决方案6】:

          似乎格式被过度指定了......(使用可变长度字段来指定可变长度字段的长度)。

          如果你使用 GCC,我建议

          if (sscanf(serialized,"%c%d:%as",&type,&len,&dest)<3) return -1;
          /* use type, dest; ignore len */
          free(dest);
          return 0;
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-05-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-08-05
            • 1970-01-01
            相关资源
            最近更新 更多