【问题标题】:When/why is it a bad idea to use the fscanf() function?何时/为什么使用 fscanf() 函数是个坏主意?
【发布时间】:2009-05-14 19:49:24
【问题描述】:

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

您能否详细说明何时/为什么最好使用fgets()sscanf() 来读取某些文件?

【问题讨论】:

    标签: c pointers file-io


    【解决方案1】:

    想象一个包含三行的文件:

       1
       2b
       c
    

    使用fscanf() 读取整数,第一行会读得很好,但在第二行fscanf() 会让你留在'b',不知道从那里做什么。您需要一些机制来越过垃圾输入以查看第三行。

    如果你做一个fgets()sscanf(),你可以保证你的文件指针一次移动一行,这样处理起来稍微容易一些。通常,您仍应查看整个字符串以报告其中的任何奇数字符。

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

    【讨论】:

    • 请将gets() 更改为fgets()gets() 永远不应该被使用。
    • 一定是打错字了 :) 感谢您的理解。
    【解决方案2】:

    当你匹配字符文字时,这种情况就会发挥作用。假设你有:

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

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

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

    话虽如此,这仅在找到错误的实际来源很重要时才重要。在知道存在格式错误的输入错误就足够的情况下,fscanf() 实际上优于编写自定义解析代码。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        有两个原因:

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

        新手C 程序员经常对指针和“address-of”运算符感到困惑,并且经常在需要的地方省略&,或者在不需要的地方“为了更好的衡量标准”添加它。这会导致他们很难找到的“随机”段错误。这不是scanf() 的错,所以我将其从我的列表中删除,但值得牢记。

        23 年后,我仍然记得当我开始 C 编程并且不知道如何识别和调试这些类型的错误时,我感到非常痛苦,并且(作为一个花了多年时间向初学者教授 C 的人)很难向还不了解指针和堆栈的新手解释它们。

        任何向新手C程序员推荐scanf()的人都应该被无情地鞭打。

        好吧,也许不是无情,但某种鞭刑肯定是为了;o)

        【讨论】:

        • 声明“将指针作为参数,但没有长度限制”具有误导性:对于大多数类型,大小是固定的(%i%d%lf)因此不需要长度限制.一个例外是使用%s 读取字符串。但即便如此,也可以通过在%s 之间添加一个数字来指定限制:%99s 用于声明为char s[100] 的字符串。
        【解决方案5】:

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

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

        【讨论】:

          【解决方案6】:

          基本上,没有办法告诉该函数超出您为其分配的内存区域的范围。

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

          【讨论】:

          • 虽然缓冲区溢出是 scanf() 系列函数的一个问题,但它们与这里提出的问题无关。 -1
          • “您能否详细说明为什么使用 fgets() 和 sscanf() 读取文件可能更好。”我正在扩展他的问题。我拒绝你过于雄心勃勃的“-1”
          • 我认为“扩展原因”是指您的答案应该基于已经提出的前提,即文件指针问题。如果他想要其他原因,他就不会与问题的来源联系起来,也不会引用问题的相关部分。
          • 啊。我将其理解为真正的“其他原因”,因为他已经在问题中解释了原因;)我猜对同一个问题的不同解读。
          • 由于引用的原始问题与使用 fscanf() 读取整行有关,因此与 fgets() 的比较以及对缓冲区的关注不仅仅是在哪里的问题文件指针无法匹配,尽管这是另一个线程中引用的示例。
          猜你喜欢
          • 2011-11-29
          • 1970-01-01
          • 1970-01-01
          • 2020-08-10
          • 2013-10-02
          • 1970-01-01
          • 2017-01-31
          • 2011-11-23
          • 1970-01-01
          相关资源
          最近更新 更多