sscanf说明符%[]和缓冲区溢出

时间:2014-12-28 08:09:59

标签: c embedded scanf

对不起这个"另一个" 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...

请注意我在嵌入式系统中;我买不起非常大的缓冲区来提高溢出限制......

有没有办法解决我的问题?或者我应该对每个字符进行手动循环以手动进行解析吗?

3 个答案:

答案 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_valuep_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 );