10

我制作了一个简单的资源打包器,用于将我的游戏资源打包到一个文件中。一切都很顺利,直到我开始编写解包器。我注意到我打包的 .txt 文件 - 26 字节 - 很好地从资源文件中出来,没有任何问题,所有数据都保留了。但是,当读取我打包在资源文件中的 .PNG 文件时,前 5 个字节是完整的,而其余的则完全无效。

我将其追溯到打包过程,我注意到 fread 仅读取 .PNG 文件的前 5 个字节,而我终其一生都无法弄清楚原因。它甚至会触发“EOF”,表明该文件只有 5 个字节长,而实际上它是一个 787 字节的 PNG 小多边形,100 像素 x 100 像素。

我什至通过制作一个单独的应用程序来简单地将这个 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;
}

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

4

2 回答 2

21

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

Windows平台,我猜?

用这个:

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

而不是这个:

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

请参阅msdn以获取说明。

于 2010-08-22T19:47:38.727 回答
8

扩展SigTerm 的正确答案,这里有一些背景说明为什么您会获得以文本模式打开 PNG 文件的效果:

PNG 格式对其8 字节文件头的解释如下:

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

   (十进制) 137 80 78 71 13 10 26 10
   (十六进制) 89 50 4e 47 0d 0a 1a 0a
   (ASCII C 表示法)\211 PNG \r \n \032 \n

此签名既可将文件识别为 PNG 文件,又可立即检测常见的文件传输问题。前两个字节在期望前两个字节唯一标识文件类型的系统上区分 PNG 文件。第一个字节被选为非 ASCII 值,以减少文本文件被误识别为 PNG 文件的可能性;此外,它还捕获清除位 7 的错误文件传输。字节 2 到 4 命名格式。CR-LF 序列捕获改变换行序列的错误文件传输。control-Z 字符在 MS-DOS 下停止文件显示。最后的换行检查 CR-LF 转换问题的逆。

我相信在文本模式下,fread()当它读取包含 Ctrl+Z 字符的第六个字节时,调用被终止。Ctrl+Z 历史上在 MSDOS(以及之前的 CPM)中用于指示文件的结束,这是必要的,因为文件系统将文件的大小存储为块数,而不是字节数。

通过以文本模式而不是二进制模式读取文件,您触发了防止意外使用TYPE命令显示 PNG 文件的保护。

您可以做的一件有助于诊断此错误的事情是使用fread()略有不同。您没有测试fread(). 你应该。此外,您应该这样称呼它:

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

所以这nread是实际写入缓冲区的字节数。对于文本模式下的 PNG 文件,它会在第一次读取时告诉您它只读取 5 个字节。由于文件不可能那么小,因此您可能会知道发生了其他事情。缓冲区的剩余字节从未被 修改过fread(),如果您将缓冲区初始化为其他填充值,就会看到这一点。

于 2010-08-23T02:07:02.867 回答