【问题标题】:Difference between fread(&c, 1, 1, input) and fgetc(input) for reading one bytefread(&c, 1, 1, input) 和 fgetc(input) 读取一个字节的区别
【发布时间】:2018-05-08 03:56:37
【问题描述】:

我目前正在尝试读取一个 PNG 文件,一次一个字节,当我使用 fread((void*), size_t, size_t, FILE*)fgetc(FILE*) 时,我得到了不同的结果。

我本质上想“一次读取一个字节,直到文件结束”,我以两种不同的方式这样做。在这两种情况下,我都通过以下方式以二进制模式打开我想要的图像:

FILE* input = fopen( /* Name of File */, 'rb');

并将每个字节存储在一个字符中,char c

fread:while( fread(&c, 1, 1, input) != 0) //read until there are no more bytes read

fgetc: while( (c = fgetc(input)) != EOF) //Read while EOF hasn't been reached

fread 的情况下,我读取了我需要做的所有字节。读取功能在文件末尾停止,我最终打印了所有 380,000 个字节(这是有道理的,因为输入文件是 380kB 文件)。

但是,在fgetc 的情况下,一旦我到达一个值为ff 的字节(即-1,宏EOF 的值),我就会停止。

我的问题是,如果两个函数都在做同样的事情,一次读取一个字节,那么 fread 怎么知道继续读取,即使它遇到了一个值为EOF 的字节?在此基础上,如果在读取文件时传递了EOF,fread 如何知道何时停止?

【问题讨论】:

  • EOFint 值为 -1,而不是 char 值。通常,EOF 根本不是合法的字节值。如果将其存储到char,当然,您无法区分,但这就是为什么fgetc 返回int,而不是char,因为0xff 是一个完全合法的返回值 表示EOF
  • 你的结论基于一个错误的前提,即“ff(即-1,宏EOF的值”是错误的。这是c 的类型导致了这种混乱。将c 的类型更改为int,问题就会消失。谷歌类似“为什么fgetc() 返回int 而不是char?”。
  • @ShadowRanger 好的。这更有意义......我现在理解它的方式是:EOF 的类型为INT,即 4 个字节。 EOF 的值为 -1,这意味着四个连续字节的模式为“0xff。因此,因为在 fgetc 场景中,我只读取了 1 个0xff,并将EOF 字节截断为 1字节,我欺骗程序(错误地)提前完成......这是对问题的合理解释吗?
  • @ricardo:不。文件系统知道 fike 有多长,因为它在文件元数据中保留了长度。它不会在文件数据之后放置任何类型的标记值。如果您尝试读取文件并且读取指针位于文件末尾,则将设置 FILE 结构中的 eof 标志,并且将从您用于读取文件的任何库函数返回 EOF 指示。在fgetc 的情况下,EOF 返回值为负数,不能与字符代码混淆,因为fgetc 始终返回非负字符代码,即使char 是有符号类型。
  • ...这就是为什么fgetc 返回int 而不是charfgetc 的许多可能返回值不能表示为(有符号的)char。将这样的值存储在(有符号的)char 中是未定义的行为,尽管 gcc 可靠地符号扩展了返回值的最后 8 位。 (在大多数架构中,“符号扩展”是一种奇特的说法,即“只是假装第 8 位是符号”,但理论上 char 类型有可能超过 8 位。)

标签: c file file-io stdio


【解决方案1】:

fgetc 返回 int,而不是 charEOF(以及许多实际的字符代码)不能存储在 char 中,尝试这样做会导致未定义的行为。所以不要那样做。将返回值存储在 int 中。

【讨论】:

  • “我们不能将 EOF,一个 int,存储在一个 char 中”……这是为什么呢?如果我们只是简单地将 int 类型转换为 char,如果值在 [-128, 127] 范围内,我们不会丢失任何信息。我见过的许多帖子都将 EOF 的值设为 -1,这可以用 1 字节的值来表示。在您的评论中,您提到如果我们在文件末尾,则 fgetc 的返回值为负数。为什么我们不能将其存储在签名字符中?在我看来,char 可以存储 [-128, 127] 范围内的任何值,所以如果它在范围内,我认为我们没有理由不能使用 char。
  • @ricardo:fgetc 有 257 个可能的返回值; 256 个不同的字符和 EOF。一个 8 位数据类型可以有 256 个不同的值。所以至少有一个不合适。另外,正如我所说,fgetc 总是返回字符的正值。其中 128 个不能用带符号的字符表示。如果 char 是无符号的,则可以表示所有字符代码,但 EOF(始终为负数)不能。
  • @Ricardo:如果您想使用 fgetc 遍历每个字符,请按照您的方式进行操作。只需将 c 设为 int 而不是 char。 while(!feof(input)) 几乎永远不会正确,因为 feof 直到 报告 EOF 之后才设置。所以最后一次迭代仍然会返回 EOF; feof 调用并不能保护你。
  • @Ricardo:作为附带问题,如果您计划使用来自ctype.h 的各种函数(例如isalpha),您需要注意他们希望他们的论点是@ 987654333@ 任何字符的非负值,或EOF(将始终返回false,但它是合法输入)。换句话说,他们完全期望fgetc 返回的int。如果参数恰好是负数,将char 传递给isalpha 的常见用法将产生未定义的行为,因为对于US-ASCII 以外的字符,它将与签名char 一起使用。
  • 这一事实并未广为人知的原因是 GNU libc 实现在隐藏此错误时遇到了一些麻烦,它允许将负参数降至 -128。但是,其他 libc 实现可能不会那么慷慨,所以我不清楚 glibc 实际上是在帮您一个忙。
猜你喜欢
  • 1970-01-01
  • 2018-02-19
  • 1970-01-01
  • 2020-10-02
  • 1970-01-01
  • 2019-10-21
  • 1970-01-01
  • 2015-11-14
  • 2020-11-30
相关资源
最近更新 更多