在二进制数据文件的标头中放入啥

Posted

技术标签:

【中文标题】在二进制数据文件的标头中放入啥【英文标题】:What to put in a binary data file's header在二进制数据文件的标头中放入什么 【发布时间】:2010-09-29 19:23:51 【问题描述】:

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

我需要更改结构,因此我想添加一个包含文件版本号的标头,该版本号将在结构更改时递增。由于我正在这样做,我还想添加一些其他信息。我正在考虑结构的大小、字节顺序,也许还有创建二进制文件的代码的 svn 版本号。还有什么有用的补充吗?

【问题讨论】:

【参考方案1】:

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

很高兴拥有固定大小标题的功能(在开始时):

通用“长度”字段(包括标题)。 CRC32 之类的东西(包括标头)。

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

好吧,当您查看RFC 2126(第 4.3 章)中描述的 TPKT 格式之类的内容时,也许您会有更多的理解。

【讨论】:

【参考方案2】:

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

总而言之 - 将格式化的文本元数据放在文件末尾,并确定其存储在其他位置的长度。

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

2) 对元数据使用 XML、YAML 或 JSON。如果在末尾附加元数据,这将特别有用/安全,因为读取文件的人不会仅仅因为它以 XML 开头就自动认为它都是 XML。

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

【讨论】:

【参考方案3】:

对于大型二进制文件,我会认真考虑 HDF5(谷歌)。即使您不想采用它,它也可能为您在设计自己的格式时指明一些有用的方向。

【讨论】:

我听说过 HDF5,但一直没有时间研究它。它似乎是科学计算的标准。它可能对我们需要的东西来说有点过分了,因为我们的数据只是一个结构的多个副本。对于更复杂的东西,我会考虑。 我用它来做一些简单的事情,它非常有用。 (虽然我希望他们有更简单的 Java 绑定。)他们为您提供标准工具 + MATLAB 可以解析它。【参考方案4】:

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

否则我会使用其他人已经说过的内容:文件创建的时间戳,创建文件的机器的标识符,基本上是您可以想到的用于诊断目的的任何其他内容。理想情况下,您将包括结构格式本身的定义。如果您经常更改结构,那么维护正确版本的代码以读取各种格式的旧数据文件是一件很痛苦的事情。

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

我喜欢 HDF5 的另一个原因是您可以选择使用压缩,这需要非常短的时间,并且如果数据变化缓慢或除了一些错误的光点外几乎相同,则可以为您带来巨大的存储空间优势趣味性。

【讨论】:

【参考方案5】:

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

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

【讨论】:

【参考方案6】:

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

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

截断/未终止的文件是 很容易被发现。 元数据页脚通常可以 附加到现有文件而不 影响他们的阅读代码。

我使用的最简单的元数据页脚如下所示:

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"
;

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

读取文件时,寻找到最后 - sizeof(FileFooter)。阅读页脚,并验证 magicString。然后,根据 metadataFooterSize 回溯,读取元数据。根据文件中包含的页脚大小,您可以对缺失的字段使用默认值。

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

【讨论】:

这是一种我从未想过的有趣方法。您甚至可以将 MetadataFooter 设为 XML 字符串,并获得二进制数据文件的所有优点,并且仍然拥有一个易于扩展的存储元数据的方案。 @KeithB:啊!这是我没有考虑过的技术。我喜欢这样;-)【参考方案7】:

对于大文件,您可能需要添加数据定义,这样您的文件格式就可以自我描述了。

【讨论】:

一般来说这是个好主意。在我们的具体情况下,数据库是数据的“官方”版本。二进制文件仅用于将数据输入模拟。【参考方案8】:

除了架构版本控制所需的任何信息外,添加对问题进​​行故障排除时可能有价值的详细信息。例如:

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

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

【讨论】:

【参考方案9】:

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

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

【讨论】:

我打算同时包含文件创建器的文件格式版本和 SVN 版本,这样如果我们以后在创建器的某些版本中发现错误,很容易找出哪些数据文件是受影响。 OK - 这也有效(事实上,这是个好主意)。但关键是文件格式有一个版本号,它与以给定格式编写文件的代码的版本号是分开的。【参考方案10】:

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

【讨论】:

记录计数作为另一项检查是个好主意。数据没有移动太多,所以 CRC 可能是矫枉过正。另一方面,写入文件的时间很容易计算,不需要每次读取文件时都检查。我们可以为此提供一个独立的实用程序。 我在文件 END 中存储元数据的技术(见下文)可以快速解决这个问题。 我倾向于在开头而不是结尾存储元数据,因为根据我的经验,文件结尾更容易丢失。如果是这种情况,部分恢复损坏数据的机会会在开始时通过元数据得到改进。 此外,将元数据放在文件末尾可能会使附加到文件的速度更慢或更复杂。【参考方案11】:

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

所以现在不要在标题中添加东西,因为它可能很有趣。您只是在创建必须维护的代码,但没有什么实际价值。

【讨论】:

【参考方案12】:

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

【讨论】:

以上是关于在二进制数据文件的标头中放入啥的主要内容,如果未能解决你的问题,请参考以下文章

在persistence.xml 的jta-data-source 中放入啥?

在 ArrayLists () 中放入啥 [关闭]

在 python 模块文档字符串中放入啥? [关闭]

当使用 conda-build 构建 conda 包并且我的代码使用纯 python 库时,我需要在 meta.yaml 文件中的 build/host/run 中放入啥?

如何在整个程序中调用我的游戏循环以及在 tick() 中放入啥

我应该在 REST 客户端中放入啥 JSON 来测试给定的 Schema