试图理解 fgets()

Posted

技术标签:

【中文标题】试图理解 fgets()【英文标题】:Trying to Understand fgets() 【发布时间】:2017-08-11 04:41:10 【问题描述】:

我正在编写一个程序来逐行读取文件。我知道 fgets() 会一直读到换行、n-1 个字符或 EOF。让我困惑的是 fgets() 如何知道前进到新行/它在文件中的位置?

为了更清楚,我知道在 Java 中你必须明确告诉扫描器读取下一行。但是在 C 语言中,我只是将 fgets() 放在一个 while 循环中,并相信它可以完成它的工作。

我如何在逻辑上让自己确信 fgets() 实际上正在前进到下一行,而不是在那个 while 循环中一遍又一遍地读取同一行(是的,我知道我可以打印出来)?

【问题讨论】:

鉴于 fgets() 接受一个文件指针,它只会将该指针推进到文件中,刚好超过下一个换行符。如果您要对该文件使用任何读取功能(不仅仅是 fgets),这些功能将从 fgets() 离开文件指针的位置开始:就在换行符之后。 “不在那个 while 循环中一遍又一遍地读取同一行”:从某种意义上说,你不能。您只需信任实现库功能的人。因此,如果不知何故,在某个地方,你有一个 April-1st 样式库,你的场景可能会发生(并且 ftell 之类的功能同样会被破坏)。但这当然不应该。 关闭和打开文件是重新启动的一种方式,是的。使用rewindfseek 可能更容易(也快一点)。阅读 fgets、rewind、fseek 等的手册页。或者看看你是否可以在 C 中找到关于文件/流访问的相当详细的指南。The C IO chapter of Wikibooks 可能是一个好的开始。 一般来说,试着把一个文件想象成你移动的一系列连续的字节,一路读取它们。如果你的文件很短,你可以在一张纸上画出各种动作,看看事情是如何移动的,包括 fgets。 关于 fgets 有一点需要注意:如果 size 参数比行短,fgets 会读到那个点。下一次迭代,它将从那个点继续。它不会跳过该行的其余部分并从下一行开始。因此,如果您想读取整行,请始终确保 size 参数(和输入缓冲区)与您要读取的最长行(包括换行符)一样长。 【参考方案1】:

fgets() 的语义非常简单:从FILE* 流指针读取字节,直到:

目标数组已满(n-1 字节已被读取并存储到其中)。 从流中读取了一个换行符并将其存储到目标数组中。 已到达文件末尾或发生读取错误(EOFfgetc() 调用或等效函数返回)。在fgets() 返回NULL 之后,可以通过调用ferr() 和/或feof() 来区分文件结束和读取错误情况。

空终止符总是在从流中读取字节后存储到数组中,除非立即到达文件末尾(fgets() 返回NULL)或者缓冲区大小指定为大小为 0。

fgets() 的行为就像使用 fgetc() 一次读取一个字节一样。

fgets() 使用从流中读取的字节并将它们存储到目标数组中。如果流完全支持搜索,除非您使用rewind()fseek()fsetpos() 显式向后搜索到流中,否则它不能再次读取相同的字节。附加到常规文件的流通常支持查找,但以文本模式打开的文件需要在某些系统上进行特定处理,尤其是 Microsoft Windows。另请注意,ungetc() 推回流中的字节将在实际流中的任何字节之前由fgets() 读取。

fgets() 将换行符存储到目标数组中,如果该行足够短以适合空终止符之前的数组。

fgets() 如果行超过目标数组中的可用空间,则将长行分成更小的块。处理长行很棘手且容易出错。请注意,当fgets() 由于目标数组中空间不足而读取部分行时,对fgets() 的下一次调用将继续从上一次调用停止处的同一行读取。

如果从fgets() 返回时,目标数组不以换行符结尾,则发生以下情况之一:

当前行太长,只能从流中读取数组中适合的字节数。 读取的行是文件中的最后一行,并且文件没有以换行符结尾。 从文件中读取了一个空字节。

对这些情况处理不当可能会导致潜在的未定义行为和/或可利用的缺陷。

【讨论】:

只能搜索常规文件吗?或者,更重要的是,标准是否规定了文件何时应支持定位请求的任何保证? 不错的答案。注意:fgets() 可以用ungetc() 重新读取最后一个字符。这个答案省略了关于罕见输入错误的讨论 - 这对于最初的 fgets() 理解不是必需的 - 除了知道它存在。 @david_bowling:没有任何保证。 @chux:我还向普通读者隐瞒了面向广泛的流遇到编码错误的潜在问题的血腥细节......这是有充分理由的。 这就是我需要知道的关于 fgets() 的所有信息。谢谢!

以上是关于试图理解 fgets()的主要内容,如果未能解决你的问题,请参考以下文章

fgets 提供资产,试图重新分发 vs2010 MFC 应用程序

关于s_gets函数的理解

试图理解 inputAccessoryView 方法

试图理解条件变量

试图理解C中的递归

递归 - 试图理解