如何使用sscanf将变量存储在文件的一行中?

时间:2014-10-30 13:34:27

标签: c scanf

我想在此行中扫描并存储变量: 11.0.0.0, 255.0.0.0, 10.1.0.1, eth9 netIdnetMaskgatewayinterface

sscanf(buff1,"%s %s %s %s",netId,netMask,Gateway,Iface);

使用,我能够存储这些变量,但是如果有上述示例中提到的逗号({{1}}),我该如何存储这些变量?

4 个答案:

答案 0 :(得分:4)

你必须仔细确定你想要的东西。它比你想的更难,但它可以做到。 %s的问题在于它会读取第一个空格字符。逗号不是空格,因此它将包含在%s扫描的字符串中,然后输入中没有逗号以匹配格式字符串中的逗号。所以,你需要寻找一系列'不是逗号'。这是一个'扫描设置'。

if (sscanf(buff1," %[^,], %[^,], %[^,], %s", netId, netMask, Gateway, Iface) != 4)
    …data was malformed…

格式中的前导空格会跳过输入字符串中的可选前导空格,如%s会跳过前导空格。


作为Zack中的comment注释,此代码不会保护您免受缓冲区溢出的影响。由于您没有显示任何变量的定义,因此无法知道这是否是一个问题。如果你有:

char buff1[64];
char netId[64];
char netMask[64];
char Gateway[64];
char Iface[64];

然后显然没有一个单独的字段可以大于输入缓冲区,并且不可能溢出。 OTOH,如果各个字段小于缓冲区,Zack是正确的,你可以溢出缓冲区。

有(至少)两种方法可以避免这个问题。首先,假设每个目标缓冲区长度为16个字节(而不是如上所示的64个),那么这个修改后的代码将是安全的:

if (sscanf(buff1," %15[^,], %15[^,], %15[^,], %15s",
           netId, netMask, Gateway, Iface) != 4)
    …data was malformed…

这仍然可以在Iface元素之后的缓冲区末尾留下一些字节,但在其他方面是安全的。请注意,转换规范中指定的大小比数据定义中的大小小1;这允许空终止符。

替代方案使用POSIX sscanf() feature:m'赋值分配'修饰符。在这种情况下,您将指针传递给char *scanf(),并分配正确的内存量:

char *netId = 0;
char *netMask = 0;
char *Gateway = 0;
char *Iface = 0;

if (sscanf(buff1," %m[^,], %m[^,], %m[^,], %ms",
           &netId, &netMask, &Gateway, &Iface) != 4)
    …data was malformed…

free(netId);
free(netMask);
free(Gateway);
free(Iface);

请注意,如果转换失败,m修饰符分配的所有内存将在sscanf()返回之前释放。但是,如果第三次分配失败,则无法保证第一次和第二次分配的指针不变。因此,如果整体转换失败,则不应释放任何已分配的内存。

答案 1 :(得分:1)

您不应该使用sscanf执行此操作,因为您永远不应该使用*scanf。有几个原因;直接相关的是,使用*scanf无法可靠地进行错误恢复,并且可以使用%s%[...]格式描述符而无需指定目标缓冲区的大小,使它们只是和臭名昭着的gets一样危险。

我个人会用一般形式的手动代码

来做这件事
char *p = buf, *q;
for (q = p; *q && *q != ','; q++) {}
if (!*q) syntax_error();
*q = '\0';
netId = strdup(p);

p = q+1;
while (*p == ' ' || *p == '\t') p++; 
for (q = p; *q && *q != ','; q++) {}
if (!*q) syntax_error();
*q = '\0';
netMask = strdup(p);

// etc

标准库中有一些功能(例如strsepstrchr似乎就像他们可以在上面改进一样,但如果你真的尝试使用它们发现它们不会使您的代码更短或更容易阅读。

在POSIX系统上,另一个合理的选项是regex.h接口:

// ERROR HANDLING OMITTED FOR BREVITY

// outside the loop
regex_t linere;
regcomp(&linere, 
        "^([0-9.]+),[ \t]*([0-9.]+),[ \t]*([0-9.]+),[ \t]*([a-zA-Z0-9_]+)$",
        REG_EXTENDED);

// inside the loop
regmatch_t rm[5];
regexec(&linere, buf, 5, rm, 0);

netId = malloc(rm[1].rm_eo - rm[1].rm_so + 1);
memcpy(netId, buf + rm[1].rm_so, rm[1].rm_eo - rm[1].rm_so);
netId[rm[1].rm_eo - rm[1].rm_so] = '\0';

// etc

如果解析工作比这更复杂一点,可能是时候到达lexyacc

答案 2 :(得分:-1)

您需要在格式字符串上使用%[^,]来指定要复制到','的字符串。

完全像:

sscanf(buff1,"%[^,], %[^,], %[^,], %[^,]", netId, netMask, Gateway, Iface);

EDIT1: 感谢Jonathan的评论','在格式字符串中更改为[^,]

答案 3 :(得分:-2)

你简单地给你想要忽略的角色,比如像这样的逗号

sscanf(buff1,"%s,%s,%s,%s",netId,netMask,Gateway,Iface); 

忽略它们(未读取),scanfsscanf都会查找引号内所有内容的完全匹配, 例如 如果您尝试将字符串读为

char str[20];
 scanf("hi%s",str);

你必须输入输入作为&#39; himystring &#39;,存储在 str 中的内容将是&#39; mystring < / EM>&#39;,

希望能为你清除它!