二进制与文本模式下的文件操作 -- 性能问题



在许多项目中,我看到数据对象/结构以二进制模式写入文件,然后再次以二进制模式从文件中检索它们。

我想知道他们为什么以二进制模式这样做?文本模式和二进制模式之间的性能差异吗?如果没有,那么何时使用二进制模式或文本模式?

二进制

更快。 考虑一个以 32 位(4 字节)存储的整数,例如 123456。 如果你要把它写成二进制(这就是它在计算机中的表示方式),它将需要 4 个字节(忽略项目之间的填充以在结构中对齐)。

要将数字写为文本,必须将其转换为字符串(转换和存储内存有一些开销),然后将其写出,至少需要 6 个字节,因为有 6 个字符来重新表示数字。 这不包括任何其他填充,例如用于对齐的空格或用于读取/分隔数据的分隔符。

现在,如果您认为您有数千个项目,则额外的时间可能会加起来并需要更多的空间,这将需要更长的时间来读入,然后在将值读入内存后,还有额外的时间转换回二进制进行存储。

文本的优点是,对于人们来说,它更容易阅读,而不是尝试读取二进制数据或数据的十六进制转储。

如果您的程序是唯一将要使用该文件的程序,则可以使用二进制文件"按原样"保存内部结构。

但是,如果您想与其他程序或通过 Internet 交换文件,那么二进制格式就不是那么好了。例如,想想大端与小端机器的问题。此外,文件或数据的接收方很可能无法访问您的代码和结构,因此基于文本的格式可能更容易解析和实现到自己的结构中。

关于性能,直接读取和写入内部结构确实会更快,因为您不必将它们(也称为封送处理)转换为另一种格式。

从历史上看,二进制模式是提供或多或少透明的访问到底层流;文本模式"规范化"为标准文本表示,其中行由单个'n'终止字符。 此外,系统可能会对尺寸施加限制二进制文件,例如要求它是 128 的倍数或512 字节。 (第一个是CP/M的情况,是许多CP/M中的第二个十二月操作系统的。 文本文件没有此限制,并且在以下情况下:操作系统强加了它,库通常会引入额外的文本文件的文件字符结尾。 (即使在今天,大多数窗口库在读取文本时识别文件的旧 CP/M 结尾,0x1A模式。 由于这些考虑因素,文本模式仅在一组有限的二进制值。 (但是如果你将 200 字节写入二进制文件,当您重新阅读它时,您可能会得到 256 或 512。 历史二进制应仅用于其他结构化的文本,因此您可以识别逻辑结束,并忽略这些额外的字节。

此外,您可以在以二进制打开的文件中任意查找模式;你只能寻求起点,或者你已经找到的位置以前在文本模式下记住。 (这是因为行结尾映射意味着位置之间没有简单的关系在文件中,以及文本流中的位置。

请注意,这与输出是否格式化是正交的:如果使用 << 输出(并使用 >> 输入),则 IO 将被格式化,无论打开文件的模式如何。 和格式始终是文本;IO流旨在操纵流文本,并且仅对非文本输入和输出有有限的支持。

今天,情况发生了一些变化:在许多情况下,我们期望什么我们写的是从其他机器上可读的,这假设一个井定义的格式,这可能不是本机使用的格式。 (因此,对于例如,互联网期望两个字节序列0x0D,0x0A为一行结尾,这与Unix内部使用的不同,许多其他操作系统。 如果可移植性是一个问题,您通常定义一个格式化,明确写入,并使用二进制模式来确保你写正是所写的;同样,在输入上,您使用二进制格式,并手动处理约定。 如果你只是写信给本地磁盘,不共享,但是,文本模式很好,而且有点更少的工作。

同样,这两者都适用于文本。 如果你想要二进制格式,你必须使用二进制模式,但这还远远不够。 你必须自己实现所有格式化的 IO。 在这种情况下,我通常不要使用std::istreamstd::ostream(其抽象是文本),而是定义我自己的流类型,派生自std::ios_base(对于错误处理约定),并使用std::streambuf(对于物理 IO)。

最后,不要忽视所有IO都格式化为某些方式。 只需将内存块写入文件就意味着格式是当前实现碰巧给你的任何内容(这通常是无证的,这意味着您可能无法以后再读)。 如果您所做的只是溢出到磁盘,并且您唯一会阅读它的时间是使用相同的程序,使用同一编译器的相同版本,使用相同的编译器选项,然后您可以只转储内存,前提是有问题的内存只是 POD,并且不包含任何指针。 否则,您必须定义(和文档)您使用的格式,并实现它。 在这种情况下,我建议使用现有的格式,如 XDR,而不是发明自己的格式:很多更容易编写"使用 XDR 格式"作为文档,而不是描述所有不同内容的实际位和字节布局类型。

如果在文本模式下读/写文件,则操作文本。它可能是编码错误和特定于操作系统的格式更改的主题,尽管有时它可能工作正常。但是,在二进制模式下,您将不会满足这些限制。此外,文本模式可能会对n字符做一些有趣的事情,例如将它们替换为 nr .

例如,Fopen参考文献说:

对于文本文件,取决于环境 应用程序运行时,可能会在 输入/输出操作,使其适应系统特定的文本文件 格式。在许多环境中,例如大多数基于 UNIX 的系统,它 将文件作为文本文件或二进制文件打开没有区别; 两者的处理方式完全相同,但差异化是 建议使用以获得更好的便携性。

只有少数操作系统会受到二进制和文本模式之间选择的影响。 Unix 或 Linux 系统都没有对文本模式做任何特殊的事情——也就是说,文本与二进制相同。

特别是Windows和VMS在文本模式下转换数据。 Windows 在写入文件时将n转换为rn,在读取时将转换为。 VMS 具有要观察的文件记录结构,因此在默认模式下,它会n转换为记录分隔符。

在它不同的地方,二进制更快。 如果它没有不同,那就没有区别。

在二进制模式下,您可以使用字节大小(考虑 256),而在文本模式下,它几乎不超过 100 个字符。显然,您将获得两倍以上的存储数据大小。
此外,在某些情况下,您必须遵守结构规范,例如IPv4等网络数据包。

让我们举个例子

//No padding
typedef struct abc
{
 int a:4
 char b;
 double c;
} A[]={{.a=4,.b='a',.c=7.45},{.a=24,.b='z',.c=3.2}} ;

在文本模式下存储位字段不是很困难吗?显然你会丢失很多东西。

但是,

您可以使用MIME将数据对象保存为文本格式,但是在二进制模式下转换需要一个额外的例程;性能重创。

二进制格式更准确地存储数字,因为它们存储在精确的内部表示中。保存数据时没有对话,因此保存速度要快得多。

最新更新