如何捕获 sscanf'd 字符串的长度?

Posted

技术标签:

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

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

    1 个字符 一个整数(一位或多位数字) 1 个冒号 一个字符串,其长度来自#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++(如果有足够的响应,我实际上希望看到竞争方法)。

【问题讨论】:

【参考方案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;

【讨论】:

以上是关于如何捕获 sscanf'd 字符串的长度?的主要内容,如果未能解决你的问题,请参考以下文章

如何在cpp中解析从套接字捕获的数据包?

正则表达式:如何使范围匹配最小可能长度?

opencv摄像头捕获视频

C#如何捕获异常并检查它是否包含字符串?

如何将单引号 (') 捕获到 JSON.parse 的字符串中?

如何重定向 python 解释器输出并将其捕获到 C++ 程序中的字符串中?