【问题标题】:C: fscanf - infinite loop when first character matchesC: fscanf - 第一个字符匹配时的无限循环
【发布时间】:2010-09-30 09:14:28
【问题描述】:

我正在尝试使用 fscanf 解析文本 (CSS) 文件并提取与此模式匹配的所有语句:

@import "some/file/somewhere.css";

为此,我设置了以下循环:

FILE *file = fopen(pathToSomeFile, "r");
char *buffer = (char *)malloc(sizeof(char) * 9000);

while(!feof(file))
{
    // %*[^@] : Read and discard all characters up to a '@'
    // %8999[^;] : Read up to 8999 characters starting at '@' to a ';'.
    if(fscanf(file, "%*[^@] %8999[^;]", buffer) == 1)
    {
        // Do stuff with the matching characters here.
        // This code is long and not relevant to the question.
    }
}

只要文件中的第一个字符不是“@”,它就可以完美运行。 (从字面上看,CSS 文件中第一个“@”字符前的一个空格将使代码运行良好。)

但是如果 CSS 文件中的第一个字符是'@',那么我在调试器中看到的是一个无限循环——执行进入 while 循环,命中 fscanf 语句,但没有进入 'if ' 语句(fscanf 失败),然后永远继续循环。

我相信我的 fscanf 格式化程序可能需要一些调整,但我不确定如何进行。关于为什么会发生这种情况的任何建议或解释?

谢谢。

【问题讨论】:

  • 您的字符串开头似乎缺少%。这是复制粘贴错误,还是错误?
  • 哎呀。我在尝试用格式化程序诊断问题时删除了第一个 %。我已经把它放回了上面的代码中,但是它的存在并不能解决问题。
  • 这个问题既不是关于objective-c也不是关于cocoa。所以你应该删除这些标签。
  • 对 Bryan 的不相关建议:将 // This code is long and ... 替换为设置一些变量并调用一两个函数
  • 不要使用scanf 编写解析器。

标签: c scanf


【解决方案1】:

我不是scanf 模式语法方面的专家,但我对你的解释是:

  • 匹配非空的非'@'字符序列,然后
  • 匹配最多包含 8999 个非';' 字符的非空序列

是的,如果您的字符串以'@' 开头,那么第一部分将失败。

认为如果您以一些空格开始您的格式字符串,那么fscanf 将吃掉您的数据字符串中的任何前导空格,即简单地" %8999[^;]"

【讨论】:

  • 你是对的 - " %8999[^;]" 将导致前导空格被丢弃。
  • 奥利,谢谢。我相信这已经在当前的格式化程序中被捕获:“%*[^@] %8999[^;]” 在不以“@”开头的文件上,fscanf 会正确丢弃空格。
  • @Bryan:确实。但是您当前的格式化程序不适用于以'@' 开头的行,而我的建议可以!
【解决方案2】:

Oli 已经说过 fscanf 失败的原因。由于失败是 fscanf 的正常状态,因此您的繁忙循环不是 fscanf 失败的结果,而是缺少处理它的结果。

即使您的格式正确(在您的特殊情况下),您也必须处理 fscanf 失败,因为您无法确定输入始终与格式匹配。实际上,您可以确定存在比匹配输入更多的不匹配输入。

【讨论】:

  • +1:这是一个很好的观点。 fscanf 不是您想要的,除非您可以绝对保证您的输入文件是 100% 格式正确的。最好使用自定义解析器。
  • 谢谢蒂洛。我知道我可以处理失败。但我的问题是输入文件是 100% 格式正确的;当我使用 fscanf('@' 符号)查找的第一个字符之前有零个字符时,它就会失败。
  • @Bryan:我回答的重点(没有回答你原来的问题;))是,如果你像你的问题一样在循环中使用 fscanf,那么你不仅“可以”处理错误,但你“必须”处理它。因为——正如现实所表明的——你不能保证 fscanf 不会失败,即使你认为它不应该在你的输入上失败。如果您在提出问题时已经理解这一点,那么像“为什么在该输入行上这个 fscanf 失败”这样的问题而不提及循环可能会更清楚。
【解决方案3】:

您的格式字符串执行以下操作:

  • 读取(并丢弃)1 个或多个非@ 字符
  • 读取(并丢弃)0 个或多个空白字符(由于格式字符串中的空格)
  • 读取并存储 1 到 8999 个非; 字符

很遗憾,没有用于从用户定义集中读取“零个或多个”字符的格式说明符。

如果您不关心一行中有多个 @include 语句,您可以将代码更改为读取单行(使用 fgets),然后从中提取 @include 语句(如果第一个字符不等于@,您可以将当前格式字符串与 sscanf 一起使用,否则,您可以使用 sscanf(line, "%8999[^;]", buffer))。

如果应该正确处理一行中的多个@include 语句,您可以使用getc 检查要读取的下一个字符,然后使用ungetc 将其放回。

【讨论】:

  • 谢谢巴特。不幸的是,我确实关心单行上的多个@import 语句,并且 90% 的时间,该行会以“@”开头。如果真的没有办法让 fscanf 将第一个字符识别为匹配的字符,看来我必须手动完成。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-18
  • 2016-01-01
  • 2014-07-20
  • 1970-01-01
  • 1970-01-01
  • 2013-11-28
相关资源
最近更新 更多