【发布时间】:2009-05-14 19:49:24
【问题描述】:
在an answer 中有一个有趣的声明:“使用fscanf() 函数几乎总是是个坏主意,因为它会使你的文件指针在失败时留在未知位置。我更喜欢使用fgets() 获取每一行,然后使用sscanf() 。”
您能否详细说明何时/为什么最好使用fgets() 和sscanf() 来读取某些文件?
【问题讨论】:
在an answer 中有一个有趣的声明:“使用fscanf() 函数几乎总是是个坏主意,因为它会使你的文件指针在失败时留在未知位置。我更喜欢使用fgets() 获取每一行,然后使用sscanf() 。”
您能否详细说明何时/为什么最好使用fgets() 和sscanf() 来读取某些文件?
【问题讨论】:
想象一个包含三行的文件:
1
2b
c
使用fscanf() 读取整数,第一行会读得很好,但在第二行fscanf() 会让你留在'b',不知道从那里做什么。您需要一些机制来越过垃圾输入以查看第三行。
如果你做一个fgets()和sscanf(),你可以保证你的文件指针一次移动一行,这样处理起来稍微容易一些。通常,您仍应查看整个字符串以报告其中的任何奇数字符。
我自己更喜欢后一种方法,尽管我不同意“使用fscanf() 几乎总是一个坏主意”的说法......fscanf() 对于大多数事情来说都很好。
【讨论】:
gets() 更改为fgets()。 gets() 永远不应该被使用。
当你匹配字符文字时,这种情况就会发挥作用。假设你有:
int n = fscanf(fp, "%d,%d", &i1, &i2);
考虑两个可能的输入“323,A424”和“323A424”。
在这两种情况下,fscanf() 都将返回 1,读取的下一个字符将是 'A'。无法确定逗号是否匹配。
话虽如此,这仅在找到错误的实际来源很重要时才重要。在知道存在格式错误的输入错误就足够的情况下,fscanf() 实际上优于编写自定义解析代码。
【讨论】:
当 fscanf() 失败时,由于输入失败或匹配失败,文件指针(即文件中下一个字节将被读取的位置)被留在一个位置,而不是它应该在的位置让 fscanf() 成功。这在顺序文件读取中通常是不可取的。一次读取一行导致文件输入是可预测的,而单行故障可以单独处理。
【讨论】:
有两个原因:
scanf() 可以让stdin 处于难以预测的状态;如果不是不可能的话,这会使错误恢复变得困难(这对fscanf() 来说不是问题);和scanf() 系列都将指针作为参数,但没有长度限制,因此它们可以超出缓冲区并更改恰好在缓冲区之后的不相关变量,从而导致看似随机的内存损坏错误很难理解,发现和调试,特别是对于经验不足的 C 程序员。新手C 程序员经常对指针和“address-of”运算符感到困惑,并且经常在需要的地方省略&,或者在不需要的地方“为了更好的衡量标准”添加它。这会导致他们很难找到的“随机”段错误。这不是scanf() 的错,所以我将其从我的列表中删除,但值得牢记。
23 年后,我仍然记得当我开始 C 编程并且不知道如何识别和调试这些类型的错误时,我感到非常痛苦,并且(作为一个花了多年时间向初学者教授 C 的人)很难向还不了解指针和堆栈的新手解释它们。
任何向新手C程序员推荐scanf()的人都应该被无情地鞭打。
好吧,也许不是无情,但某种鞭刑肯定是为了;o)
【讨论】:
%i、%d、%lf)因此不需要长度限制.一个例外是使用%s 读取字符串。但即便如此,也可以通过在% 和s 之间添加一个数字来指定限制:%99s 用于声明为char s[100] 的字符串。
使用
fscanf()函数几乎总是一个坏主意,因为它会在失败时将文件指针留在未知位置。我更喜欢使用fgets()输入每一行,然后使用sscanf()。
您始终可以使用ftell() 找出文件中的当前位置,然后决定从那里做什么。基本上,如果您知道可以期待什么,请随时使用fscanf()。
【讨论】:
基本上,没有办法告诉该函数不超出您为其分配的内存区域的范围。
已经出现了许多替代品,例如 fnscanf,它试图通过指定读取器写入的最大限制来修复这些功能,从而使其不会溢出。
【讨论】: