Sscanf没有回归我想要的东西

时间:2014-05-23 21:05:10

标签: c scanf

我有以下问题: sscanf没有以我想要的方式返回。 这是sscanf

sscanf(naru,
       "%s[^;]%s[^;]%s[^;]%s[^;]%f[^';']%f[^';']%[^;]%[^;]%[^;]%[^;]"
       "%[^;]%[^;]%[^;]%[^;]%[^;]%[^;]%[^;]%[^;]%[^;]%[^;]%[^;]%[^;]"
       "%[^;]%[^;]%[^;]%[^;]%[^;]%[^;]",
       &jokeri, &paiva1, &keskilampo1, &minlampo1, &maxlampo1,
       &paiva2, &keskilampo2, &minlampo2, &maxlampo2, &paiva3,
       &keskilampo3, &minlampo3, &maxlampo3, &paiva4, &keskilampo4,
       &minlampo4, &maxlampo4, &paiva5, &keskilampo5, &minlampo5,
       &maxlampo5, &paiva6, &keskilampo6, &minlampo6, &maxlampo6,
       &paiva7, &keskilampo7, &minlampo7, &maxlampo7);

它正在扫描的字符串:

const char *str = "city;"
                  "2014-04-14;7.61;4.76;7.61;"
                  "2014-04-15;5.7;5.26;6.63;"
                  "2014-04-16;4.84;2.49;5.26;"
                  "2014-04-17;2.13;1.22;3.45;"
                  "2014-04-18;3;2.15;3.01;"
                  "2014-04-19;7.28;3.82;7.28;"
                  "2014-04-20;10.62;5.5;10.62;";

所有变量都存储为char paiva1[22]等;但是,sscanf并不能正确存储除城市以外的任何内容。我一直试图阻止;处的每个变量。 任何帮助如何正确存储日期等的帮助将不胜感激。

或者,如果有更聪明的方法,我可以接受建议。

2 个答案:

答案 0 :(得分:2)

存在多个问题,但BLUEPIXY遇到了第一个问题 - 扫描集符号不符合%s

格式的第一行是:

"%s[^;]%s[^;]%s[^;]%s[^;]%f[^';']%f[^';']%[^;]%[^;]%[^;]%[^;]"

就目前而言,它会查找以空格分隔的单词,然后是[^;](这是自我矛盾的;字符串后面的字符是字符串的空格或结尾。)

第一个修复方法是正确使用扫描设置:

"%[^;]%[^;]%[^;]%[^;]%f[^';']%f[^';']%[^;]%[^;]%[^;]%[^;]"

现在您遇到的问题是,第一个%[^;]会扫描所有内容,直到字符串或第一个分号结束,第二个%[;]不会留下任何内容。

"%[^;]; %[^;]; %[^;]; %[^;]; %f[^';']%f[^';']%[^;]%[^;]%[^;]%[^;]"

这会查找一个分号的字符串,然后是分号,然后是可选的空格,然后重复三个项目。除了增加一个长度来限制字符串的大小,防止溢出,这些都很好。 %f没问题。以下材料再次查找奇数字符序列。

但是,当查看数据时,它似乎包含一个城市,然后是七组“日期加三个数字”。

使用结构数组(如果你已经使用过这些结构),或者一组4个并行数组和一个循环,你会做得更好:

char jokeri[30];
char paiva[7][30];
float keskilampo[7];
float minlampo[7];
float maxlampo[7];

int eoc;   // End of conversion
int offset = 0;
char sep;
if (fscanf(str + offset, "%29[^;]%c%n", jokeri, &sep, &eoc) != 2 || sep != ';')
    ...report error...
offset += eoc;

for (int i = 0; i < 7; i++)
{
    if (fscanf(str + offset, "%29[^;];%f;%f;%f%c%n", paiva[i],
               &keskilampo[i], &minlampo[i], &maxlampo[i], &sep, &eoc) != 5 ||
        sep != ';')
        ...report error...
    offset += eoc;
}

另见How to use sscanf() in loops

现在您拥有可以管理的数据。这组29个单独命名的变量是一个可怕的想法;使用它们的代码将是可怕的。

请注意,扫描集转换规范将字符串限制为比jokeripaiva数组元素的大小短的最大长度。


您可能合理地想知道代码在%c%n之前使用&sep&eoc的原因。有一个原因,但它很微妙。假设sscanf()格式字符串是:

"%29[^;];%f;%f;%f;%n"

此外,假设数据中存在第三个数字丢失后的分号存在问题。对sscanf()的调用会报告它已成功完成4次转换,但不会将%n计为作业,因此您无法判断sscanf()未找到分号,因此根本没有设置&eoc;该值将从之前的sscanf()调用中遗留下来,或者只是未初始化。通过使用%c将值扫描到sep,我们会在成功时返回5,我们可以确定%n也是成功的。代码检查sep中的值实际上是分号而不是其他内容。

您可能想要考虑在分号之前和%c之前的空格。它们将允许转换一些其他数据字符串,否则将无法匹配。格式字符串中的空格(扫描集外部)表示可能出现可选空格的位置。

答案 1 :(得分:1)

我会使用strtok函数将;作为分隔符将字符串分解为多个部分。如此长的格式字符串可能是将来出现问题的根源。