为什么以及如何编写和读取二进制文件?

3

我正在编写一个游戏项目作为一项爱好,目前需要将一些资源数据(例如.BMP)存储到自己的文件格式中,以便我的游戏可以解析所有内容并加载到屏幕中。

对于读取BMP,我首先读取头文件,然后读取每个像素的RGB数据,并有一个数组[width][height]来存储这些值。

有人告诉我应该以二进制形式保存这些类型的数据,但没有理由。我已经了解了二进制及其含义(数据的0-1表示方式),但为什么要用它来保存.BMP数据呢?如果我稍后要在游戏中读取它,难道不会增加更多的复杂性,甚至可能会减慢加载过程吗?

最后,如果最好以二进制形式保存(从我在其他游戏资源文件中研究的情况来看,似乎是这样),那么我该如何在C++中读写二进制数据?我看到很多问题,但有很多不同的方法适用于许多不同类型的变量,所以我想问哪种方法最好/更符合C++的风格?


bmp是一种规范可供他人使用的格式,如果您想使用它,需要按照该格式编写,以便其他通常可以打开bmp文件的程序也能够读取它们。如果只想能够读取这些文件,请将其保存在任何您想要的格式中,但不要以一种可能会让人误认为是其他格式的方式命名文件。 - PlasmaHH
这里的二进制很可能不是指文本。 - Alexey Frunze
1个回答

9
你完全弄反了。计算机处理器以二进制级别操作数据。在计算机中,一切都是二进制的。为了处理人类可读格式的数据,我们编写函数来跳过许多步骤,使二进制数据看起来像人类所理解的东西。因此,如果你将.BMP数据存储为文本文件,则实际上是让计算机做更多的工作,将.BMP数据从其自然的二进制形式转换为文本,然后从文本形式转换回二进制以显示它。
事实是,你越能处理原始二进制形式的数据,你的代码运行得越快。少进行转换意味着更快的代码。但显然存在一个权衡:如果你需要查看数据并理解它而不必拿出魔法解码器环,那么你可能希望将其存储为文本文件。但这样做时,我们必须了解,必须进行转换处理,以使人类可读的文本对处理器有意义,而处理器只处理纯二进制数据。
另外,如果你已经知道这一点或者大致知道这一点,并且你的问题是“为什么应该以二进制模式而不是文本模式打开我的.bmp文件”,那么这是因为以文本模式打开文件会要求平台执行CRLF到LF的转换(根据平台需要),以便在内部字符串处理级别上,你所处理的只是'\n'字符。如果文件由二进制数据组成,你不希望发生这种转换,否则它将在读取时破坏文件中的数据。在这种状态下,大多数数据都没问题,而且大部分时间都可以正常工作,但偶尔你会遇到一对十六进制形式为0x0d、0x0a(十进制13、10)的字节,它们将被转换为仅为0x0a(10),并且你将丢失读取的一个字节的数据。因此,请务必以二进制模式打开二进制文件!
正如你现在明白的那样,计算机中的数据以二进制格式存储。是的,这意味着数据由0和1组成。但是,在编程时,你通常不需要直接处理0和1,除非你因某种原因要进行按位逻辑操作。例如,类型为int的变量是一组单独的位,每个位可以是0或1。它也是一组字节,假设每个字节有8个位,则int通常由2、4或8个字节组成,具体取决于您的平台和编译器选项。但是,您把int看作一个整体,而不是单独的0和1。如果您将该int以最纯净的形式写入文件,则字节(因此也是位)以未转换的原始形式写出。但是,您也可以将它们转换为ASCII文本并以这种方式写出。如果您在屏幕上显示int,您肯定不想只看到单独的0和1,因此您会按其ASCII形式打印它,通常解码为十进制数。您同样可以将相同的int以其十六进制形式打印出来,即使是相同的数字,结果看起来也会有所不同。例如,以十进制表示,您可能有十进制值65。同样的值在十六进制中为0x41(或者如果我们知道它是基于16进制的,则只需写为41)。如果我们以ASCII形式显示它(并且仅考虑2、4或8字节的低字节,即将其视为一个char),则相同的值是字母'A'。
在接下来的讨论中,忘记我们之前提到的是一个 int,现在假设我们正在讨论的是一个 char,也就是 1 个字节(8 个比特)。假设我们仍然有同样的值,65、0x41 或 'A',怎么看都可以。如果你想将这个值发送到文件中,你可以以其原始形式发送,也可以将其转换为文本形式发送。如果你以原始形式发送,它将占用文件中的 8 个比特(1 个字节)。但如果你想以文本形式将其写入文件,则需要将其转换为 ASCII 码,具体取决于你要写入的格式和实际值(在这种情况下为 65),它将占用 1、2 或 3 个字节。比如说,如果你想以十进制 ASCII 码形式不带填充字符地将其写入,那么值 65 将占用 2 个字节:一个用于 '6',一个用于 '5'。如果你想以十六进制形式打印它,它仍将占用 2 个字节:一个用于 '4',一个用于 '1',除非你在前面加上 "0x",此时它将占用 4 个字节,一个用于 '0',一个用于 'x',一个用于 '4',还有一个用于 '1'。或者假设你的 char 的值是 255(char 的最大值):如果我们以十进制 ASCII 码形式将其写入文件,它将占用 3 个字节。但是,如果我们以十六进制 ASCII 码形式写入相同的值,它仍然只占用 2 个字节(如果我们在前面加上 "0x",则为 4 个字节),因为十六进制中的值 255 是 0xFF。将此与以原始二进制形式写入 8 位比特(char)进行比较:按定义,一个 char 占用 1 个字节,因此无论其值如何,在二进制形式下,它仅占用文件的 1 个字节。

我明白了,感谢提供的信息。但是如果我要用图像的RGB信息填充我的数组[宽度][高度],难道我不需要将其转换为可读格式,以便正确地填充数组吗? 还是说我应该使用0-1进行这些操作?我猜我需要将二进制转换为字符(0-255范围内)来表示值。 - Danicco
是的,我理解使用二进制文件编写的好处,非常感谢!大多数我搜索到的地方都给了我同样的定义,但我无法领会从中节省空间的好处。 - Danicco
@Danicco - 这不仅仅是一个“节省空间”的问题,尽管这确实有价值,更多的是一个“无需执行任何转换”的问题。此外,如果您要将位图单独存储在文件中,则可能希望使程序以真正的 .bmp 格式编写和读取位图,以便您可以轻松地替换图像等。但是,如果您要在单个文件中放置一堆杂项,那么这显然并不重要。 - phonetagger
@Danicco - 其实,我撤回之前的说法。这不是一个“节省空间”的问题,而是一个“减少文件I/O”的问题。读写文件涉及的I/O越少,速度就越快。是的,不需要将ASCII转换为原始数据有所帮助,但我认为更大的问题是读写额外字节所带来的文件I/O负担。 - phonetagger

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