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

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

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

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

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

现在,如果你考虑它你有几千个项目,额外的时间可以累加,需要更多的空间,这需要更长的时间来读入,然后有额外的时间转换回二进制存储后,你已经读取了值进入记忆。

文本的优点是,它更容易为人们阅读,而不是尝试读取数据的二进制数据或hex转储。

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

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

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

从历史上看,二进制模式是提供对底层流的或多或少的透明访问; 文本模式“标准化”为标准文本表示,其中行由单个'\n'字符终止。 另外,系统可以对二进制文件的大小施加限制,例如通过要求它是128或512字节的倍数。 (第一个是CP / M的情况,这是DEC OS中的第二个。)文本文件没有这个限制,并且在操作系统强加它的情况下,库通常会引入一个额外的文件结束字符用于文本文件。 (即使在今天,大多数Windows库在文本模式下读取时都能识别旧的CP / M文件结尾0x1A。)由于这些考虑,文本模式仅在一组有限的二进制值上定义。 (但是如果你把200个字节写入二进制文件,当你重新读取它时可能会得到256或512.从历史上看,二进制文件只能用于其他结构化的文本,这样你才能识别逻辑结束,忽略这些额外的字节。)

此外,您可以在以二进制模式打开的文件中任意搜索; 你只能在文本模式中寻找开头或者你以前记忆的位置。 (这是因为行结束映射意味着文件中的位置与文本流中的位置之间没有简单的关系。)

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

今天,情况有所改变:在许多情况下,我们希望我们编写的内容可以从其他机器读取,这些机器假定格式很好,可能不是本机使用的格式。 (因此,例如,互联网期望两个字节序列0x0D,0x0A作为行结束,这与Unix和许多其他操作系统内部使用的不同。)如果可移植性是一个问题,通常定义一种格式,写它是明确的,并使用二进制模式来确保你所写的内容正是所写的; 类似地,在输入时,您使用二进制格式,并手动处理约定。 但是,如果您只是写入未共享的本地磁盘,则文本模式可以正常工作。

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

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

如果以文本模式读取/写入文件,则表示您正在操作文本。 它可能是编码错误和特定于操作系统的格式更改的主题,但有时可能会正常工作。 但是,在二进制模式下,您将无法满足这些限制。 此外,文本模式可能会使用\n字符做有趣的事情,例如用\n\r替换它们。

例如, Fopen参考说:

对于文本文件,根据应用程序运行的环境,在输入/输出操作中可能会发生一些特殊的字符转换,以使它们适应系统特定的文本文件格式。 在许多环境中,例如大多数基于UNIX的系统,将文件作为文本文件或二进制文件打开没有区别; 两者的处理方式完全相同,但建议区分以获得更好的便携性。

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

Windows和VMS特别是以文本模式转换数据。 Windows在写入文件时将\n转换为\r\n ,在读取时转换为反向。 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以文本格式保存数据对象,但是需要额外的例程才能以二进制模式进行转换; 表现受到重创。

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