何时/为什么使用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,从而允许它不会溢出。