将原始字节写入文件是否有危险?[关闭]


12

我正在解决Pearls编程中的一个问题-具体来说,是一个程序的实现,该程序对最多包含10,000,000个整数的文件进行排序(第1栏,问题3)。由于这本书没有指定数据应如何存储在文件中,因此我正在考虑将整数存储为原始字节(还有其他一些约束使原始字节成为不错的选择)。我以前从未在如此低的水平上工作过,所以我想知道是否有什么危险值得我注意。例如,当我将原始字节写入文件时,是否需要担心意外使用某种文件结束序列?

编辑:

我现在意识到我的问题有多广泛。我的意思确实是更具灾难性的问题,例如意外覆盖磁盘上的其他文件。抱歉,我本来不清楚。


6
请注意,《 Programming Pearls》是一本非常古老的书;您可以轻松地将整个10 ^ 7整数读入现代台式机的内存中,进行排序,然后再次写入。要获得本章的初衷,请将您随时阅读的数量限制为总数的一小部分。或者,将文件大小增加到大约10 ^ 10整数。
Caleb 2014年

3
实际上,当我听到“危险”一词时,我会想到使我的PC爆炸,删除我的银行帐户之类的事情。而且,我猜想最有可能假设-只要您的程序不用于控制空中客车或发电厂-当您尝试所想的时,就不会发生真正的“危险”。
布朗


2
@delnan几年前,当流行EOF角色的神话时,我想起了当时很多复制程序都基于“复制到EOF角色”的复制保护系统。一些程序会将其他要检查的数据放在关联文本文件的EOF标记之后,但在文件分配的末尾之前。复制程序不会复制多余的数据来验证全新安装……嗯……怀旧。

危险?就像“如果这样做,我的计算机会炸毁”一样吗?不。
jwenting

Answers:


11

您将遇到的唯一危险是小字节序和大字节序(无论是最重要的字节还是最重要的字节先写入)。但是,如果您留在同一环境中,将不会有任何问题。除了一般确保编写/解析往返。

文件系统旨在处理任何字节序列。


2
+1为最后一行。我不确定大/小问题是唯一的问题-例如,OP可能会混淆整数之间的边界在哪里。但是反正很好的答案。
Caleb 2014年

27

不,实际上这是多少种文件格式。这样的二进制文件的常见示例包括图像和音乐/音频文件。

为了维护文件和从文件读取的数据的完整性,请确保遵循以下准则:

  • 始终使用相同的模式(文本或二进制)打开文件(读取或写入)。主要区别在于文本模式关心换行符,并且在读取文件时可能会“剔除”换行符(取决于所使用的特定库)。文本模式还可以执行Unicode转换,这很可能会阻塞非Unicode数据。
  • 读取非字符串数据时,请确保使用与写入相同的数据类型进行读取。例如,如果文件的前四个字节是描述性整数,请确保使用采用/提供整数的方法进行读写,以确保始终如一地对待它。相同的数据类型在不同的机器上可能具有不同的大小,并且在同一机器上混合数据类型也可以更改数据的含义(例如,将较长整数中间的一位解释为符号位)。
  • 字节序:如果您使用的库不能始终如一地处理此问题,则可能需要自己处理。例如,对于多字节类型,Java始终使用网络字节顺序(大字节序)。C和C ++使用库实现者决定的任何东西,通常与处理器相同(Intel上的小端,其他大多数上的大端)。如果这是在一个系统上的快速练习,它并不是那么重要,但是仍然要注意并在必要时对其进行编码,这仍然是一个好习惯。

具体细节将因框架,平台和语言而异,但这应涵盖文件I / O的基本“陷阱”。


3
非字符串数据的另一个要点:确保每种类型使用一致数量的字节。在C和C ++中,int可以在2到8个或更多字节之间(实际上是八位字节)。
Bart van Ingen Schenau 2014年

这是我第二点隐含的内容,例如32 v。64位整数。它们将是不同的数据类型。

您可能要使其明确。int在两台不同的机器上可能被视为不同的数据类型并不是很明显。
Bart van Ingen Schenau 2014年

9

除了已经提到的所有陷阱之外,如果要建立一种新的二进制文件格式而不是以现有格式读写数据,那么绝对重要的是要包含一个文件头:一开始就是一个数据块文件标识明确标识文件格式并记录可能需要的任何元数据。

好的文件头至少包括三件事:

  • 至少四个字节的“ 魔术数字 ”。幻数必须是rfc2119,是文件中的前N个字节,必须从未用于可挖掘的任何其他文件格式,并且必须包含至少一个不是可打印ASCII字符的字节。有关如何设计真正彻底的幻数,请参见PNG规范。请参阅命令源代码,以获取现有魔术数的数据库,该数据库可能会像您发现的那样全面。file(1)

    魔幻数字的重点是明确地在文件中对其格式进行带内标记。如果您不包含幻数,或者这不是文件中的第一件事,则可能会导致程序将您的文件误识别为其他类型的文件,从而导致数据丢失,病毒逃逸检测等。灾难。

  • 文件格式版本的指示。即使您认为不必再大幅度修改文件格式,也可以在魔术数字之后加上两个字节,00 00并以一定的字节序记录这是一个16位版本号(您可以随意选择,但选择一个,并在整个文件中坚持使用),如果后续数据的含义发生根本变化,则该值将递增。您未来的自我会感谢您。

    (PNG规范在这里采用了不同的方法,指定冻结了块格式,并且所有对该格式的将来更改都将采用新的块类型的形式。这也是有效的,但是我建议使用简单的幻数+版本号方法二进制数据处理的初学者。PNG的设计者正在汲取数十年的图像格式经验。)

  • 一种将任意元数据嵌入文件中的机制。这很简单,就是让两个字节是从标头末尾到实际数据开头的16位偏移量,其间的所有内容都可以解释为RFC 822中的UTF-8键值对。 (也就是说,“ Tag: value\n”-如果您选择这条路线,建议不要折叠长行)。同样,PNG非常聪明。


无需编排自己的文件格式...只需将数据存储为图像即可。您可能需要更改尺寸(例如10k x 1k),以便对其进行支持。或者您可以使用FITS。如果您的数据比仅单个阵列更复杂,则可以使用HDFCDFNetCDF
2014年

我建议保持简单。256个不同的版本就足够了,如果不能满足要求,则可以将其他版本设计为255版的子版本。类似地,对于元数据,将它们添加到实际需要的版本中就足够了。@乔图像??? 您可以通过预先使所有人感到困惑来避免潜在的格式混乱!
maaartinus 2014年

@maaartinus将version字段设置为两个字节会强制格式设计器预先提交字节序。元数据的空间应始终为二进制格式的版本0,否则您将陷入ID3之类的可怕困境。对于PNG规范关于通过新块类型(而不是格式版本颠簸)的可扩展性的逻辑,我深表同情。但是,块结构文件本身带来了很多复杂性,因此我不建议在简单情况下推荐它们。我很想建议HDF作为一种通用格式的处理,有很多这些问题了。
zwol 2014年

2

不同的体系结构具有不同的整数表示形式。这里的主要风险是将整数A的字节表示形式保存在机器A中,然后尝试读回该字节并将其内容解释为机器B中的整数。如果机器A和B的整数大小和/或字节序不同,则您这很可能导致未定义的行为(例如,在C中)或异常。

由于这只是一个编程示例,而不是“真实的”程序,因此这并不是真正的问题。如果这是一个真实的程序,则滚动自己的应用程序特定的二进制格式通常不是一个好主意;有更好的解决方案,例如SQLite或基于字符串的序列化格式(例如JSON,YAML,XML等)。对于单个值,将其转换为字符串就足够了;对于简单列表,您可以每行保存一个字符串,并在读回时将其拆分为换行符。


大致同意,但是JSON或XML将大大增加包含10 ^ 7数字的文件的大小。同样,它们通常一次全部被读取和解析,但是所讨论的章节涉及对包含更多数据的文件进行排序,而这些数据超出了可用内存的容量。
Caleb 2014年

这取决于您在做什么。有时候,SQL对性能的影响要比滚动实用程序重要。上次我做的记录很小,很可能要邻居。从磁盘上读取一个更大的块通常几乎不会花费任何费用,因此,如果我想要一条记录,我会将1000读入缓存。我的记录几乎可以肯定是彼此相邻的,而使用SQL磁盘头会在各处弹跳。
罗伦·佩希特尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.