我对所提供的数据进行了一些修改,我很确定发生了什么:
FILE *inptr = fopen(infile, "r");
and
FILE *outptr = fopen(outfile, "w");
以文本模式打开文件。
这是我从 Microsoft 的 C API 中了解到的一种特殊行为,似乎这也适用于 mingw TDM-GCC(我很难相信这一点,直到转储的数据让我相信我的怀疑是正确的)。
微软的C API中的文件I/O区分文本模式和二进制模式:
-
fopen(infile, "rt")
and fopen(outfile, "wt")
以文本模式打开文件。
-
fopen(infile, "rb")
and fopen(outfile, "wb")
以二进制模式打开文件。
-
fopen(infile, "r")
and fopen(outfile, "w")
默认为文本模式。
在文本模式下,文件读取会替换所有 Microsoft 行结束符 ("\r\n"
) 通过 Unix 行尾 ("\n"
)以及写作相反("\n"
变成"\r\n"
).
如果内容是纯文本,这是合理的,但它可能会破坏二进制内容的输出,其中一个字节0x0d
每当一个字节被插入0x0a
(具有任何含义)发生在数据流中。
为了证明这一点,
- 我下载了您的示例文件(不幸的是以 PNG 格式上传)
- 将文件(回)转换为 24 位 BMP(使用 GIMP)
-
为每个进行了十六进制转储:
$ hexdump -C IkW6FbN.bmp >IkW6FbN.bmp.txt
$ hexdump -C jnxpTwE.bmp >jnxpTwE.bmp.txt
终于加载了IkW6FbN.bmp.txt
and jnxpTwE.bmp.txt
into WinMerge进行比较。
![Snapshot of WinMerge showing where wrong contents start in output file](https://i.stack.imgur.com/lRFia.png)
如快照所示,输入和输出文件的前 14037 (0x36d5) 字节具有相同的内容。然后,输入文件“意外”包含三个字节0a 0a 0a
输出文件的位置0d 0a 0d 0a 0d 0a
。因此,各个原始像素(以及所有后续像素)都会被损坏。
(正如您可能已经猜到的那样,0a
是换行符的十六进制值'\n'
, 0d
回车符之一'\r'
.)
顺便提一句。输出文件可能比输入文件长一点(由于插入了 CR 字节)。 BMP 查看器可能会忽略这一点,因为 BMP 标头准确说明了原始图像需要多少字节(并且会忽略额外的字节)。
正如您可能已经认识到的那样,您应该更改fopen()
打电话给
FILE *inptr = fopen(infile, "rb");
and
FILE *outptr = fopen(outfile, "wb");
解决您的问题。
顺便提一句。 *x 操作系统(例如 Linux)上的 C API 没有文本和二进制模式的区别。相反,b
被简单地忽略(这有助于编写可移植代码)。
进一步阅读:cppreference.com 上的 fopen、fopen_s