【问题标题】:Unexpected return value from fread()来自 fread() 的意外返回值
【发布时间】:2012-07-27 20:42:00
【问题描述】:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

int main()
{
    FILE* bmp = NULL;
    uint32_t offset;
    uint8_t* temp = NULL;
    size_t read;
    unsigned int x_dim = 600, y_dim = 388;

    bmp = fopen("test_colour.bmp", "r");

    if (!bmp)
        return -1;

    /* Get the image data offset */
    fseek(bmp, 10, SEEK_SET);
    fgets((char*)&offset, 4, bmp);

    printf("Offset = %u\n", offset);

    temp = malloc(3*x_dim*y_dim*sizeof(uint8_t));

    if (!temp)
        return -1;

    /* Go the the position where the image data is stored */
    fseek(bmp, offset, SEEK_SET);

    /* Copy image data to array */
    printf("%u bytes requested!\n", 3*x_dim*y_dim);
    read = fread((void*)temp, sizeof(uint8_t), 3*x_dim*y_dim, bmp);
    printf("%Iu bytes read!\n", read);

    fclose(bmp);
    free(temp);

    return 0;
}

我正在使用上述代码将每像素 24 位 BMP 图像的 RGB 数据读取到数组中。根据 BMP 规范,从图像数据开始(在 BMP 标头之后)的文件开头的偏移量在偏移量 10 处给出。执行上述代码时,我得到以下输出。

Offset = 54
698400 bytes requested!
33018 bytes read!

偏移量输出似乎是正确的,因为文件大小为 698454 字节 (=698400+54)。但是,fread() 返回的值似乎表明无法读取整个图像数据。但是,我随后使用 temp 数组中的数据将 RGB 数据转换为灰度,然后再次将这些数据写入 BMP 文件。目视检查输出图像并不表示有任何错误,即似乎我实际上首先读取了整个输入图像,尽管fread() 似乎表示不同。

有人可以解释这种行为吗?

【问题讨论】:

  • 两个问题:1)你能在调用fread之后检查temp的内容,看看它是否真的在33018字节后停止读取? 2) 我不熟悉%Iu 格式说明符 - 你能在调试器中检查read 的实际值吗?
  • 我不认为这是导致您看到的症状的原因,但您应该使用正确的 printf 格式字符串。 size_t 的格式是 "%zu""%Iu" 是非标准的。如果你的实现不支持"%zu",你可以使用printf("%lu bytes read!\n", (unsigned long)read);
  • 我正在使用 gcc 和 MinGW 进行编译,编译器无法识别 %zu 说明符(我之前尝试过使用该说明符)。我读到必须在 Windows 中使用%Iu
  • @simon:请参阅stackoverflow.com/a/10680635/12711,了解为什么 MinGW 在支持的printf() 格式说明符上可能会有些混乱。较新的 MinGW 版本和/或较新版本的 MSVCRT.DLL 似乎支持越来越多的 C99 标准说明符。
  • “但是,我随后使用临时数组中的数据将 RGB 数据转换为灰度数据,并将此数据再次写入 BMP 文件”。你能测试一下新bmp文件的大小吗?它大于 33 KB 吗? nem文件的尺寸是600*388像素吗?

标签: c fread bmp


【解决方案1】:

(我敢打赌你在 Windows 上)

bmp = fopen("test_colour.bmp", "r");

应该是

bmp = fopen("test_colour.bmp", "rb");

如果文件在 Windows 上以文本模式打开,运行时将在碰巧碰到 0x1a (Ctrl-Z) 字节时停止读取,Windows 将其视为文本文件的 EOF 标记。即使没有按 Ctrl-Z,当 Windows 将 CR/LF 序列转换为单个 LF 字符时,您也会得到损坏的数据。

但是,我无法解释为什么您能够从读取的部分文件中获得好的图像(只是幸运?)。

您可以从缓冲区渲染图像,因为 fread() 实现确实将您请求的字节数(或几乎如此 - 数字被四舍五入到某个块大小的倍数)读入缓冲区,然后它扫描缓冲区以查找要转换的 CR/LF 序列和 Ctrl-Z EOF 标志。

所以即使fread() 返回33018,缓冲区实际上已经几乎完全写入了来自文件的数据。数据不是 100% 正确(例如,一些 CR 字符可能已被丢弃)或完整,但在这种情况下,它已经足够接近以呈现与您预期相似的图像。

当然,这只是对这个特定运行时当前行为方式的观察 - 它可能在未来(甚至在今天的所有系统上)并不总是如此。

【讨论】:

  • 我确实是,忘了在OP中提及。谢谢指出,解决了问题!你确定它真的停止阅读了吗?如前所述,我仍然能够给出整个 BMP 图像的正确灰度图像,尽管根据 fread 仅实际读取了大约 5% 的文件数据。
  • @simon:运行时可能会将您请求的字节数读取到缓冲区中,然后通过它并“修复”行尾和 EOF 翻译。但我只是猜测。
  • 我比较了生成的两张输出图像(一张用"r"打开文件,另一张用"rb"打开文件)与差异,它们不一样。从视觉上看图像,唯一的区别似乎是"r" 图像中的最后几个像素是黑色的,而它们实际上应该是白色的。所以我猜fread实际上读取的字节数比返回值指示的多,但不一定是实际请求的字节数。
  • 在 '"r"' 模式下,由于它们的值,某些字节不会被读取。所以在 '"r"' 模式下读取的字节数比在 '"rb"' 模式下读取的字节少是正常的。 '"r"' 模式仅用于从文本文件中读取。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-06-21
  • 2020-06-24
  • 2014-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多