在许多项目中,我看到数据对象/结构以二进制模式写入文件,再以二进制模式从文件中检索它们。
我想知道为什么他们要以二进制模式进行操作?文本模式和二进制模式之间有任何性能差异吗?如果没有,那么什么情况下使用二进制模式或文本模式?
二进制更快。考虑一个存储在32位(4字节)中的整数,例如123456。如果以二进制形式写出它(这就是计算机中的表示方法),它将占用4个字节(忽略结构对齐时项目之间的填充)。
如果要将数字写成文本,则必须将其转换为一串字符(有些开销需要进行转换和内存存储),然后再将其写出,至少需要6个字节,因为有6个字符来表示数字。这还不包括任何额外的填充,如空格以对齐或分隔数据。
现在,如果您考虑到您有数千个项目,附加时间可能会增加并且需要更多的空间,这将需要更长的时间来读入,然后在将值读入内存后进行二进制转换时需要额外的时间。
文本的优点在于,与尝试阅读二进制数据或数据的十六进制转储相比,它对于人来说更容易阅读。
'\0'
。 - Some programmer dude'\n'
和"\r\n"
之间进行转换。因此,如果文件中有与'\n'(10进制)
对应的某些值,则在读取时可能会返回两个字节而不是一个(13和10)。 - Some programmer dude历史上,二进制模式提供了对底层流的更或多或少透明的访问;文本模式“标准化”为一个标准文本表示,其中行以单个'\n'
字符终止。此外,系统可能会对二进制文件的大小施加限制,例如要求它是128或512字节的倍数。(第一个是CP/M的情况,第二个是许多DEC操作系统的情况。)文本文件没有这种限制,在操作系统强制执行该限制的情况下,库通常会为文本文件引入附加的文件结束符字符。(即使在今天,大多数Windows库在文本模式下读取时仍会识别旧的CP/M文件结束符0x1A。)由于这些考虑因素,文本模式仅定义了一组有限的二进制值。(但是如果你向一个二进制文件写入200字节,当你重新读取它时,你可能会得到256或512字节。从历史上看,二进制应该仅用于其他结构化的文本,以便你可以识别逻辑结束并忽略这些附加字节。)
此外,在二进制模式下,您可以任意寻找文件;而在文本模式下,您只能寻找到开头或先前记忆的位置。(这是因为行结束映射意味着文件中的位置与文本流中的位置之间没有简单的关系。)
请注意,这与输出是否格式化无关:如果您使用<<
(和使用>>
输入),则IO是格式化的,而不管文件以哪种模式打开。并且格式化始终是文本的;iostreams旨在操作文本流,并且仅对非文本输入和输出提供有限的支持。
今天,情况有所改变:在许多情况下,我们希望我们编写的内容可以从其他计算机上读取,这需要一个定义良好的格式,该格式可能与本地使用的格式不同。(例如,互联网期望两个字节序列0x0D、0x0A作为行结束符,这与Unix和许多其他操作系统内部使用的不同。)如果可移植性是一个问题,您通常会定义一个格式,明确地编写它,并使用二进制模式来确保您编写的内容正是所写的内容;类似地,在输入时,您使用二进制格式,并手动处理约定。但是,如果只是写入到本地磁盘,而不共享,则文本模式就可以了,而且更少工作。
再次强调,这两种方式都适用于文本。如果你想要二进制格式,必须使用二进制模式,但这远远不够。你需要自己实现所有的格式化IO。在这种情况下,我通常不使用std::istream
或std::ostream
(它们的抽象是文本),而是定义自己的流类型,从std::ios_base
派生(用于错误处理约定),并使用std::streambuf
(用于物理IO)。
最后,请不要忽视一个事实,即所有IO都以某种方式进行格式化。仅将一块内存写入文件意味着该格式是当前实现所给出的任何格式(通常未经记录,这意味着您可能无法在将来阅读它)。如果你只是将其溢出到磁盘上,并且你唯一读取它的时间是使用相同程序、编译器版本和编译器选项编译的同一程序,则可以仅转储内存,前提是所涉及的内存仅为POD,并且不包含指针。否则,您必须定义(并记录)您使用的格式,并实现它。在这种情况下,我建议使用现有的格式,如XDR,而不是发明自己的格式:编写"使用XDR格式"作为文档要容易得多,而不是描述所有不同类型的位和字节布局。
seekg(pos)
,我几乎可以寻找文件的每个位置,对吗? - Alcottpos
是从tellg()
调用返回的值,或者pos
是0
,那么就没有问题。否则,它是未定义行为。(实际上,在Unix下它可以工作,在Windows下它会把你略微超前于你想去的地方。在其他操作系统下呢?谁知道呢。) - James Kanze\n
/\r\n
的替换会成为性能问题吗? - Alcott让我们举个例子。
//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一样以文本格式保存数据对象,但这将需要额外的例程来转换为二进制模式;性能受到影响。
A
写入文件更好吗?如果是这样,怎么做呢?只需将每个数据成员的值写入文件中作为纯文本,然后读取值以创建数据对象即可。 +1 for the code. - Alcott<XML><STRUCT><Instance n="0" type="Text"><val attr="a" bit field="4">4</val></Instance></STRUCT></XML>
,但最终您必须将其转换为二进制进行正常操作。在正常的二进制中,只需将结构的值转储到文件中即可。在读取操作期间,如果目标结构符合规格,则无需担心如何读取。随着光标向前移动,数组将不断填充。 - perilbrain\n
转换为\r\n
,在读取时则相反。VMS具有文件记录结构需要遵守,在默认模式下,它将\n
转换为记录分隔符。\n
并轻微使用其他内容,很容易构建出一个存在显著差异的测试。最糟糕的情况是,Windows会使写入的数据量增加一倍,而VMS则会疯狂地创建大量记录。 - wallyk二进制格式更准确地存储数字,因为它们以精确的内部表示方式存储。在保存数据时没有转换,因此保存速度更快。