对不起这个"另一个" sscanf问题,但我通过实验找不到任何解决方案。
这是一个我要解析的字符串,并提取由"分隔的2个子字符串:" :
char *str = "tag:R123:P1234";
此功能完成工作:
char r_value[5];
char p_value[6];
sscanf(str, "tag:%[^:]:%s", r_value, p_value);
// now r_value = "R123" and p_value = "P1234"
但是现在我想确保我不会溢出我的接收缓冲区:
sscanf(str, "tag:%[^:]:%5s", r_value, p_value);
// this is good for p_value, if I give something bigger than 5 character long it
// will be truncated, if less than 5 character long, I get it also
但问题在于%[]格式:
sscanf(str, "tag:%4[^:]:%5s", r_value, p_value);
// this will be ok if initial r_value is 4 char or less long
// but not OK if more than 4 char long, then it will be truncated,
// but p_value will not be found...
请注意我在嵌入式系统中;我买不起非常大的缓冲区来提高溢出限制......
有没有办法解决我的问题?或者我应该对每个字符进行手动循环以手动进行解析吗?
答案 0 :(得分:5)
使用strtok_r
char r_value[5];
char p_value[6];
char *token;
char *saveptr;
token = strtok_r(str, ":", &saveptr);
if (token == NULL)
return; /* there is no ":" in the string so handle failure properly */
token = strtok_r(NULL, ":", &saveptr);
if (token == NULL)
return; /* no more tokens found so handle failure properly */
strncpy(r_value, token, sizeof r_value);
r_value[sizeof(r_value) - 1] = '\0';
token = strtok_r(NULL, ":", &saveptr);
if (token == NULL)
return; /* no more tokens found so handle failure properly */
strncpy(p_value, token, sizeof p_value);
p_value[sizeof(p_value) - 1] = '\0';
您可以防止r_value
和p_value
溢出。
唯一的另一件事是您应该复制str
,因为strtok_r
需要修改它
char *str = "tag:R123:P1234";
将其更改为
char *str = strdup("tag:R123:P1234");
并记住最后free(str)
。
答案 1 :(得分:4)
sscanf()
的一个优点是,如果第一个格式字符串不能按预期工作,您可以尝试第二种(和第三种,......)格式。对于直接文件I / O变体,例如scanf()
和fscanf()
,您不会轻易获得第二次机会。
在这种情况下,我会考虑:
int n;
if ((n = sscanf(str, "tag:%4[^:]:%5s", r_value, p_value)) == 1)
n = sscanf(str, "tag:%4[^:]%*[^:]:%5s", r_value, p_value);
if (n != 2)
…report format error…
…continue with extra characters skipped…
*
中的星标%*[^:]
会抑制扫描结果的分配,转换规范不会计入sscanf()
的返回值。
如果您需要了解p_value
太长,可以使用%n
转换规范检查偏移量(注意%n
不计入返回值sscanf()
并且o
除非n == 2
,否则没有任何有用的值:
int o;
int n;
if ((n = sscanf(str, "tag:%4[^:]:%5s%n", r_value, p_value, &o)) == 1)
n = sscanf(str, "tag:%4[^:]%*[^:]:%5s%n", r_value, p_value, &o);
if (n != 2 || str[o] != '\0')
…report format error…
…continue with extra characters skipped…
scanf()
函数一般来说,尤其是sscanf()
,功能强大,灵活且难以正确使用。要小心!
答案 2 :(得分:1)
以下是使用sscanf
执行此操作的另一种方法。第一个sscanf
转换R值,同时限制为4个字符。第二个sscanf
确定R值的结束位置,不限制长度或存储结果。第三个sscanf
转换P值,从第二个sscanf
确定的索引开始。
char *str = "tag:R123:P1234";
char r_value[5];
char p_value[6];
int success = 0;
if ( sscanf( str, "tag:%4[^:]", r_value ) == 1 ) // convert and store R value
{
int n;
sscanf( str, "tag:%*[^:]%n", &n ); // find the end of the R value
if ( sscanf( &str[n], ":%5s", p_value ) == 1 ) // convert and store P value
success = 1;
}
if ( success )
printf( "%s\n%s\n", r_value, p_value );