fread意外的返回值()
#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规范,在偏移量10处给出从图像数据开始处(在BMP标题之后)开始的文件的偏移量。执行上述代码时,我会得到以下输出。fread意外的返回值()
Offset = 54
698400 bytes requested!
33018 bytes read!
由于文件大小为698454字节(= 698400 + 54),偏移量输出似乎是正确的。但是,fread()
返回的值似乎表示不能读取整个图像数据。但是,随后我使用temp
阵列中的数据将RGB数据转换为灰度并将此数据再次写入BMP文件。目测检查输出图像并不表示任何错误,即似乎我实际上首先读取了整个输入图像,尽管fread()
似乎表示不同。
有人可以解释这种行为吗?
(我敢打赌,你使用的是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 2012-07-30 07:58:18
@simon:运行时可能会将您请求的字节数读入缓冲区,然后检查它并“修复”行结束符和EOF转换。但我只是猜测。 – 2012-07-30 08:15:49
我比较了生成的两个输出图像(一个用'“r”打开文件,另一个用'“rb”')用diff打开,它们不一样。从视觉上看图像,唯一的区别似乎是''r“'图像中的最后几个像素是黑色的,而实际上它们应该是白色的。所以我猜'fread'实际上读取比返回值指示的更多的字节,但不一定是实际请求的字节数。 – simon 2012-07-30 08:27:57
有两个问题:1)你可以在调用'fread'之后检查'temp'的内容,看它是否在33018字节后实际停止读取? 2)我不熟悉'%Iu'格式说明符 - 你能在调试器中检查'read'的实际值吗? – Treb 2012-07-30 07:52:54
我不认为这是什么导致你看到的症状,但你应该使用正确的'printf'格式字符串。 'size_t'的格式是'“%zu”'。 '“%Iu”'是非标准的。如果你的实现不支持'“%zu”',你可以使用'printf(“%lu read read!\ n”,(unsigned long)read);' – 2012-07-30 07:55:23
我使用gcc和MinGW进行编译,编译器无法识别'%zu'说明符(我曾尝试使用该说明符)。我读过,必须在窗口中使用'%Iu'。 – simon 2012-07-30 08:04:47