何时/为什么使用fscanf()函数是个坏主意?

在一个答案中有一个有趣的声明:“使用fscanf()函数几乎总是一个坏主意,因为它可以在失败时将文件指针留在未知位置。我更喜欢使用fgets()来获取每一行然后是sscanf()那个。“

您可以扩展使用fgets()sscanf()读取某些文件的时间/原因可能更好吗?

想象一下有三行的文件:

  1 2b c 

使用fscanf()读取整数,第一行读取正常,但在第二行fscanf()会让你在’b’,不知道该怎么做。 您需要一些机制来移动垃圾输入以查看第三行。

如果你执行fgets()sscanf() ,你可以保证你的文件指针一次移动一行,这更容易处理。 通常,您仍然应该查看整个字符串以报告其中的任何奇怪字符。

我自己更喜欢后一种方法,虽然我不同意“使用fscanf()几乎总是一个坏主意”… fscanf()对于大多数事情来说都是完美的。

这种情况发生的情况是你匹配字符文字。 假设你有:

 int n = fscanf(fp, "%d,%d", &i1, &i2); 

考虑两个可能的输入“ 323,A424 ”和“ 323A424 ”。

在这两种情况下, fscanf()将返回1,下一个字符读取将为'A' 。 无法确定逗号是否匹配。

话虽如此,这只有在找到错误的实际来源很重要时才有意义。 如果知道输入错误的输入错误就足够了, fscanf()实际上优于编写自定义解析代码。

当fscanf()由于输入失败或匹配失败而失败时,文件指针(即,将从中读取下一个字节的文件中的位置)保留在不同于其所在位置的位置。 fscanf()成功了。 这在顺序文件读取中通常是不期望的。 一次读取一行会导致文件输入可预测,而单行故障可以单独处理。

有两个原因:

  • scanf()可以将stdin在难以预测的状态; 如果不是不可能的话,这会使错误恢复变得困难(这对于fscanf()来说不是一个问题); 和
  • 整个scanf()系列将指针作为参数,但没有长度限制,因此它们可以溢出缓冲区并更改恰好位于缓冲区之后的无关变量,从而导致看似随机的内存损坏错误,这些错误很难理解,查找和调试,特别是对于经验不足的C程序员。

新手C程序员经常对指针和“地址”操作符感到困惑,并经常省略&需要的地方,或者将其添加到“好的度量”中,而不是。 这会导致他们难以找到的“随机”段错误。 这不是scanf()的错,所以我把它从我的列表中删除了,但值得记住。

23年后,我仍然记得当我开始编程并且不知道如何识别和调试这些错误时,这是​​一个巨大的痛苦,并且(作为花了多年时间向初学者教授C语言的人)很难解释它们对一个尚未理解指针和堆栈的新手。

任何向新手C程序员推荐scanf()人都应该毫不留情地鞭打。

好吧,也许不是毫不留情 ,但某种鞭打肯定是有序的; o)

使用fscanf()函数几乎总是一个坏主意,因为它可能会在失败时将文件指针留在未知位置。 我更喜欢使用fgets()来获取每一行,然后使用sscanf()

你总是可以使用ftell()找出文件中的当前位置,然后决定从那里做什么。 基本上,如果你知道你可以期待什么,那么随意使用fscanf()

基本上,没有办法告诉该函数不要超出你为它分配的内存区域。

已经出现了许多替换,例如fnscanf,它试图通过指定读取器写入的最大限制来修复这些function,从而允许它不会溢出。