如何捕获 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 字符串的长度?的主要内容,如果未能解决你的问题,请参考以下文章