什么放在二进制数据文件的标题中

我有一个模拟读取我们创建的大型二进制数据文件(10到100的GB)。 出于速度原因,我们使用二进制 这些文件是系统相关的,从我们运行的每个系统上的文本文件转换而来,所以我不关心可移植性。 这些文件当前是POD结构的许多实例,用fwrite编写。

我需要更改结构,所以我想添加一个文件版本号的标头,它会随着结构的变化而增加。 由于我这样做,我想添加一些其他信息。 我正在考虑结构的大小,字节顺序,以及创建二进制文件的代码的svn版本号。 还有什么其他有用的东西可以添加吗?

根据我的经验,对你需要的数据进行二次猜测总是浪费时间。 重要的是以可扩展的方式构建元数据 。 对于XML文件,这很简单,但二进制文件需要更多考虑。

我倾向于将元数据存储在文件END的结构中,而不是开头。 这有两个好处:

  • 可以轻松检测到截断/未终止的文件。
  • 元数据页脚通常可以附加到现有文件而不会影响其读取代码。

我使用的最简单的元数据页脚看起来像这样:

struct MetadataFooter{ char[40] creatorVersion; char[40] creatorApplication; .. or whatever } struct FileFooter { int64 metadataFooterSize; // = sizeof(MetadataFooter) char[10] magicString; // a unique identifier for the format: maybe "MYFILEFMT" }; 

在原始数据之后,元数据页脚和文件页脚被写入。

在读取文件时,请搜索end-sizeof(FileFooter)。 阅读页脚,并validationmagicString。 然后,根据metadataFooterSize回顾并读取元数据。 根据文件中包含的页脚大小,您可以使用缺少字段的默认值。

正如KeithB指出的那样,您甚至可以使用这种技术将元数据存储为XML字符串,从而提供完全可扩展的元数据的优势,以及二进制数据的紧凑性和速度。

对于大型二进制文件,我会认真对待HDF5(Google for it)。 即使它不是您想要采用的东西,它也可能指向您设计自己的格式的一些有用的方向。

对于大型二进制文件,除了版本号之外,我倾向于记录计数和CRC,原因是大型二进制文件随着时间的推移或在传输期间比较小的二进制文件更容易被截断和/或损坏。 我最近惊恐地发现,Windows根本无法处理这个问题,因为我使用资源管理器将几百个文件中的2TB复制到连接的NAS设备,并发现每个副本上有2-3个文件被损坏(不完全复制)。

如果稍后将其他结构写入二进制文件,则该文件类型的标识符将非常有用。 也许这可能是一个短字符串,所以你可以通过查看文件(通过hex编辑器)看到它包含的内容。

如果它们那么大,我会在文件开头保留一个健康的空间(64K?)空间,并将元数据放在XML格式中,然后是文件结尾字符(Ctrl-Z for DOS / Windows,ctrl-D for unix?)。 这样,您可以使用适用于XML的各种工具集轻松地检查和解析元数据。

否则我会选择其他人已经说过的内容:文件创建的时间戳,创建它的机器的标识符,基本上你能想到的任何其他用于诊断目的的东西。 理想情况下,您将包含结构格式本身的定义。 如果您经常更改结构,那么维护适当版本的代码以阅读各种格式的旧数据文件会非常困难。

正如@highpercomp所提到的HDF5的一大优势在于,您只需要担心结构格式的变化,只要您对名称和数据类型有一些约定即可。 结构名称和数据类型都存储在文件本身中,因此您可以将C代码吹到smithereens并且无关紧要,您仍然可以从HDF5文件中检索数据。 它让你不用担心数据格式 ,更多地关注数据结构 ,即我不关心字节序列,这是HDF5的问题,但我确实关心字段名称等。

我喜欢HDF5的另一个原因是你可以选择使用压缩,这需要花费很少的时间,并且如果数据正在缓慢变化或者大部分相同,除了一些错误的有趣之处外,可以给你巨大的存储空间。

@rstevens说“文件类型的标识符”……声音建议。 传统上,这被称为幻数,并且在文件中,不是滥用的术语(与代码不同,它是滥用的术语)。 基本上,它是一些数字 – 通常至少4个字节,我通常确保这些字节中至少有一个不是ASCII – 您可以使用它来validation文件是否是您期望的类型,并且混淆的可能性很小。 您还可以在/ etc / magic(或本地等效项)中编写规则,以报告包含幻数的文件是您的特殊文件类型。

您应该包含文件格式版本号。 但是,我建议不要使用代码的SVN号码。 当文件格式没有时,您的代码可能会更改。

除了架构版本控制所需的任何信息之外,如果要解决问题,请添加可能有价值的详细信息。 例如:

  • 创建和更新文件的时间戳(如果适用)。
  • 构建中的版本字符串(理想情况下,您有一个版本字符串,在每个“官方”构建中自动递增…这与文件架构版本不同)。
  • 创建文件的系统的名称,以及可能与您的应用相关的其他统计信息

我们发现这非常有用(a)获取我们原本不得不要求客户提供的信息和(b)获取正确的信息 – 令人惊讶的是,有多少客户报告他们正在运行不同版本的软件而不是数据声称!

您可以考虑将文件偏移量放在标题中的固定位置,这会告诉您实际数据在文件中的开始位置。 这样可以在需要时更改标题的大小。

在几种情况下,我将值0x12345678放入标题中,以便我可以检测文件格式是否与正在处理它的机器的字节顺序相匹配。

由于我在电信设备配置和固件升级方面的经验表明,您只需在开始时确实需要几个预定义的字节(这很重要),它从版本(标题的固定部分)开始。 其余标题是可选的,通过指示正确的版本,您可以始终显示如何处理它。 这里重要的是你最好在文件末尾放置标题的’变量’部分。 如果您在标头上计划操作而不修改文件内容本身。 这也简化了“追加”操作,应该重新计算变量头部分。

很高兴有固定大小标头的function(在开始时):

  • 常见的“长度”字段(包括标题)。
  • 类似于CRC32(包括标题)。

好的,对于变量部分XML或标题中的一些漂亮的可扩展格式是个好主意但它真的需要吗? 我在ASN编码方面有很多经验……在大多数情况下,它的使用率都超过了。

好吧,当你看到RFC 2126 (第4.3章)中描述的TPKT格式之类的东西时,也许你会有额外的理解。

如果要在标题中放置版本号,则可以在需要更改POD结构或向标题添加新字段时随时更改该版本。

因此,现在不要在标题中添加内容,因为它可能很有趣。 您只需创建必须维护的代码,但这些代码几乎没有实际价值。

对于大型文件,您可能希望添加数据定义,因此您的文件格式将成为自我描述的。

我的变化结合了Roddy和Jason S的方法。

总之 – 将格式化文本元数据放在文件末尾,以确定其存储在别处的长度。

1)在文件的开头放置一个长度字段,以便在结束时知道元数据的长度,而不是假定固定的长度。 这样,要获取元数据,您只需读取固定长度的初始字段,然后从文件末尾获取元数据blob。

2)使用XML或YAML或JSON作为元数据。 如果在末尾附加元数据,这是特别有用/安全的,因为没有人阅读文件会自动认为它只是因为XML以XML开头。

这种方法的唯一缺点是当你的元数据增长时,你必须同时更新文件的头部和尾部,但是其他部分可能仍然会被更新。 如果它只是像上次访问日期一样更新琐事,那么元数据长度不会改变,所以它只需要就地更新。