【问题标题】:fopen() in read-only mode and its buffer只读模式下的 fopen() 及其缓冲区
【发布时间】:2014-06-13 19:55:20
【问题描述】:

考虑下面的 C 代码,尽管非常混乱:

#include<stdio.h>

int main() {
    char buf[3]; //a new, small buffer
    FILE *fp = fopen("test.txt", "r"); //our test file, with the contents "123abc"
    setvbuf(fp, buf, _IOFBF, 2); //we assign our small buffer as fp's buffer \
                                 //in fully buffered mode
    char character = fgetc(fp); // get the first character...
    character = fgetc(fp);      // and the next...
    character = fgetc(fp);      // and the next... (third character, '3')
    buf[2] = '\0'; //add a terminating line for display
    fputs(buf, stderr); //write our buffer to stderr, should show up immediately
}

编译和运行代码将打印“3a”作为我们自己指定的缓冲区 buf 的内容。我的问题是:这个缓冲区是如何被填满的? 调用 fgetc() 是否意味着多次调用,直到缓冲区填满然后停止(我们只调用了三个 fgetc,其中不应该包括现在'a')?第一个缓冲区是“12”,所以这是否意味着当进行另一个 fgetc() 调用并且指针引用缓冲区范围之外的内容时,是清除缓冲区然后用下一个数据块填充,还是简单地覆盖?我了解缓冲区大小取决于平台,因此我更关心通常情况下,读取模式下的 fopen()ed 流如何将字符拉入其缓冲区。

【问题讨论】:

  • “缓冲区是被清除然后用下一个数据块填充,还是直接被覆盖”——我不确定会有什么区别?

标签: c io stream buffer


【解决方案1】:

缓冲区,以及确切的填充方式和时间,是 stdio 包中的一个实现细节。但它可能实现的方式是 fgetc 从缓冲区中获取一个字符,如果缓冲区中有可用的字符。如果缓冲区是空的,它会通过从文件中读取(在您的情况下)另外两个字符来填充它。

所以你的第一个 fgetc 将从文件中读取 12 并将其放入缓冲区,然后返回 '1'。您的第二个 fgetc 不会从文件中读取,因为缓冲区中有一个字符,并返回 '2'。你的第三个 fgetc 会发现缓冲区是空的,所以它会从文件中读取 3a 并放入缓冲区,然后返回 '3'。因此,当您打印缓冲区的内容时,它将是3a

请注意,这里发生了两个级别的“阅读”。首先,您有 fgetc 调用,然后,在该级别之下,从文件读取的 stdio 包内的代码。如果我们假设这是在 Unix 或 Linux 系统上,第二种类型的读取是使用系统调用 read(2) 完成的。

较低级别的读数一次填满整个缓冲区,因此您不需要像调用 fgetc 那样多次调用 read。 (这就是拥有缓冲区的全部意义所在。)

【讨论】:

  • 非常简洁,谢谢!在 ungetc() 可能从前一个“块”或缓冲区部分“放回”一个字符的情况下呢?这是否意味着前一个块被再次读入缓冲区?例如在上面示例中的第三次调用之后调用 ungetc() 是否会使 read() 再次将“12”拉入流的缓冲区并将指针位置设置回“2”?
  • @RJS:我认为 ungetc 不会从文件中读取。它不会倒回输入流,而是将字符放入输入中以供稍后读取,所以我的猜测是它有一个单独的缓冲区来存放被推回的字符。或者单个变量,因为 C 标准只保证它可以使用单个字符。
  • 快速查看一个实现(Apple 的)的一些源代码似乎证实了这一点。如果他们可以将输入缓冲区中的当前指针向后移动,他们会这样做,否则他们有一个单独的 ungetc 缓冲区。 (opensource.apple.com/source/Libc/Libc-167/stdio.subproj/…)
  • 对,有点像K&R对ungetc的实现。作为最后一个相关问题,如果缓冲区是“无缓冲的”并且执行了对fgetc 的调用,那么 fgetc 从哪里获取它的数据?立即从read 返回意味着它可能是块读取默认值的较慢变体?从你所说的我不会假设 fgetc 会从文件中读取,因为它是。
  • 在没有检查任何源代码或标准文档的情况下:如果输入流没有缓冲,则没有缓冲,因此使用大小为 1 的 read(2) 调用获取单个字符,该字符由 fgetc 返回。
猜你喜欢
  • 2020-11-19
  • 1970-01-01
  • 1970-01-01
  • 2014-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多