读取.PNG文件只需前五个字节

10
我已经制作了一个简单的资源打包程序,用于将我的游戏资源打包到一个文件中。一切都进行得很顺利,直到我开始编写解包器。我注意到我打包的 .txt 文件大小为 26 字节,在不出现任何问题的情况下从资源文件中恢复,所有数据被保留。
然而,当我读取我在资源文件中打包的 .PNG 文件时,前 5 个字节完好无损,而其余部分则完全变成了空值。
我追踪了这个问题并发现 fread 只读取了 .PNG 文件的前 5 个字节,但我无法理解为什么会这样。甚至触发了 "EOF",表明该文件只有 5 个字节长,而实际上它是一个 100×100 像素的小多边形,大小为 787 字节的 PNG 文件。
我甚至通过制作一个单独的应用程序来测试此问题,只需将该 PNG 文件读入缓冲区即可,结果也是同样的,只读取 5 个字节。
这是那个小应用程序的代码:
#include <cstdio>

int main(int argc, char** argv)
{
    char buffer[1024] = { 0 };
    FILE* f = fopen("test.png", "r");
    fread(buffer, 1, sizeof(buffer), f);
    fclose(f);        //<- I use a breakpoint here to verify the buffer contents
    return 0;
}

能否有人指出我的愚蠢错误?


1
为什么不使用C++流? - GManNickG
为什么在 "buffer" 括号中间有一个零? - Billy ONeal
@Billy:为了清空缓冲区,我知道这不是标准的方法。 - Sam Blackburn
1
@Sam:这是标准的——你只需要不要零就可以了。{}是完全可以的,而且效果相同。 - Billy ONeal
@GMan:可能是因为这个问题被标记为“C”。 - Ed S.
@Ed:请查看时间戳和编辑记录。当我发表评论(以及@Billy的评论)时,它被标记为C++。 - GManNickG
2个回答

21

有人能指出我的愚蠢错误吗?

我猜是 Windows 平台?

使用这个:

FILE* f = fopen("test.png", "rb");

改为这样:

FILE* f = fopen("test.png", "r");

请参阅MSDN以获取解释。


请注意,"rb" 是 C 标准和可移植的。它与 "rt" 相对应,后者要求对流进行 "文本" 处理,这是默认设置。在任何 *nix 上,文本处理与二进制处理相同。但在 Windows 和一些其他更为晦涩的平台上,它们是不同的。简而言之,如果您正在处理二进制数据,请始终指定 "rb" 以使用 fopen() - RBerteig

8
扩展SigTerm的正确答案,以下是一些背景介绍,解释了为什么以文本模式打开PNG文件会产生该效果:
PNG格式将其8字节文件头解释如下:

PNG文件的前八个字节始终包含以下值:

   (十进制)              137  80  78  71  13  10  26  10
   (十六进制)           89  50  4e  47  0d  0a  1a  0a
   (ASCII C 表示法)    \211   P   N   G  \r  \n \032 \n
这个签名既能识别文件为PNG格式,又能立即检测常见的文件传输问题。前两个字节区分了在期望前两个字节唯一标识文件类型的系统中的PNG文件。第一个字节选择为非ASCII值,以降低文本文件被错误识别为PNG文件的概率;此外,它还捕获了清除第7位的坏文件传输。第二到第四个字节命名了格式。CR-LF序列捕获了更改换行符序列的坏文件传输。控制-Z字符停止在MS-DOS下显示文件。最后的换行符检查了CR-LF转换问题的反向情况。
我认为在文本模式下,对fread()的调用在读取包含Ctrl+Z字符的第六个字节时终止了。在MSDOS(以及之前的CPM)中,Ctrl+Z被历史上用于指示文件的结束,这是必要的,因为文件系统将文件大小存储为块数而不是字节数。通过以文本模式而不是二进制模式读取文件,您触发了防止意外使用TYPE命令显示PNG文件的保护。

你可以采用稍微不同的方式使用 fread() 函数来帮助诊断这个错误。你没有测试 fread() 的返回值,而应该这样调用它:

...
size_t nread;
...
nread = fread(buffer, sizeof(buffer), 1, f);

这样nread就是实际写入缓冲区的字节数。对于以文本模式打开的PNG文件,第一次读取时就会告诉你它只读取了5个字节。由于文件不可能那么小,你会意识到还有其他问题。缓冲区的剩余字节从未被fread()修改,如果你将缓冲区初始化为其他填充值,就可以看到这一点。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接