当使用fscanf解析单词时,我如何检查我何时跳过一行

时间:2017-06-13 20:17:05

标签: c parsing

我正在开发一个程序,该程序从文件中读取文本并将文本解析为单词并对其进行操作我将使用fscanf进行解析

while (fscanf (fp, " %32[^ ,.\t\n]%*c", word) == 1)

{
/*manipulate the text word by word 

}

我想在我找到她的哪一行找到每个单词的旁边

当我向下移动时,有没有办法检查 使用fscanf函数时?

2 个答案:

答案 0 :(得分:3)

最明智的建议是使用fgets()或POSIX getline()读取行然后考虑使用 sscanf()解析每一行。您可能需要考虑how to use sscanf() in a loop。解析该行而不是sscanf()还有许多其他选项,例如strtok_r()或不太理想的strtok() - 或者,在Windows上,strtok_s(); strspn()strcspn()strpbrk();和其他没有标准化的功能。

如果您认为必须使用fscanf(),则可能需要捕获尾随上下文。一个简单的版本是:

char c;
while (fscanf(fp, " %32[^ ,.\t\n]%c", word, &c) == 2)
    …

这会捕获单词后面的字符,假设有一个字符。如果您的文件没有以换行符结尾,则可能会丢失一个单词。错过换行也很容易。例如,如果该行以换行符之前的句号(句点)结束,则c将保留.,并且循环的下一次迭代将跳过换行符。你可以通过以下方式克服这个问题:

char s[33];
while (fscanf(fp, " %32[^ ,.\t\n]%32[ ,.\t\n]", word, s) == 2)
    …

请注意,格式字符串中的长度必须小于变量声明中的长度!

成功调用fscanf()后,字符串s可能包含多个换行符和空白,依此类推。 fscanf()函数主要不关心换行符,s的扫描集会读取一行中的多个换行符,如果这是数据文件中的内容。

如果您明确地从fscanf()捕获状态,则对于没有换行符(或标点字符)结束的文件或者导致其他问题的文件可能更敏感:

char s[33];
int rc;
while ((rc = fscanf(fp, " %32[^ ,.\t\n]%32[ ,.\t\n]", word, s)) != EOF)
{
    switch (rc)
    {
    case 2:
        …proceed as normal, checking s for newlines.
        break;
    case 1:
        …probably an overlong word or EOF without a newline.
        break;
    case 0:
        …probably means the next character is one of comma or dot.
        …spaces, tabs, newlines will be skipped without detection
        …by the leading space in the format string.
        break;
    default:
        assert(0);
        break;
    }
}

如果您开始关心!?;:'"个字符,那就更不用说{ {1}}和( - 生活变得更加复杂。事实上,在那时,)的替代方案开始变得更好。

正确使用sscanf()系列函数非常困难。它们不过是新手的工具,至少一旦你开始需要做任何复杂的事情。您可以查看A beginner's guide to not using scanf(),其中包含许多有价值的信息。我不完全相信最后几个被认为是scanf()的防弹用途的例子。 (正确使用scanf()会更容易一些,但您仍需要详细了解您的要求。)

答案 1 :(得分:1)

使用fgets()读取行,然后使用sscanf解析它们:

char buff[1024];
int lineno = 0;
int offset = 0;
while (fgets(buff, 1024, fp)) {
    lineno++;
    offset = 0;
    while (sscanf(buff + offset, " %32[^ ,.\t\n]%*c", word) == 1)
    {
    /* manipulate the text word by word */

    }
}

在第二个循环中,必须适当增加缓冲区偏移量才能正确解析行。为此,您可以使用%n来获取读取字节。