- 在开头有一些“幻数”,可以识别文件(在我的特定情况下,这还可以帮助区分“旧版”文件)
- 在开头有一个文件版本号,以便稍后更改文件格式而不会破坏兼容性
- 指定所有数据项的字节序和大小;或:包括一些空间来描述数据的字节序/大小(我倾向于使用前者)
- 可能为将来可能需要的每个文件属性保留一些空间?
请查看PNG规范。这种格式背后有一些非常好的原理。
此外,决定未来格式的重要内容:紧凑性、兼容性、允许在其中嵌入其他格式(不同压缩算法)。另一个有趣的例子是Google的协议缓冲区,其中传输数据的大小至关重要。
至于字节序,我建议您选择一种选项并坚持使用它,不允许不同的字节顺序。否则,读写库将变得更加复杂且速度更慢。
我同意以下观点:
开头的魔数。在*nix系统中非常必要:
文件版本号用于向后兼容。
字节序规范。
但你的第四个建议有些过度,因为第2个建议让你可以添加字段,只要你更改版本号(并且只要你不需要向前兼容)。
此外,其他许多答案提出的强制在您的文件上实施块结构的想法,似乎不像是二进制文件的普遍要求,而更像是解决某些有效负载问题的解决方案。
除了以上1-3之外,我还会添加以下内容:
需要简单的校验和或其他方式来检测内容是否完整。否则,您无法信任魔术字节或版本号。请注意指定哪些字节包括在校验和中。通常,您将包括文件中所有没有错误检测的字节。
记录写入文件的软件版本(包括最精细的版本号,例如构建号)。您会收到一个带有附加文件的错误报告,因为某些人无法打开它,而他们不知道何时编写了该文件,因为当时没有出现错误。但是错误出现在编写它的版本中,而不是尝试读取它的版本中。
在规范中明确指出这是二进制格式,即对于所有字节(除了魔术数字),允许使用0-255的所有值。
以下是一些可选内容:
如果您确实需要前向兼容性,则需要某种方式来表示哪些“块”是“可选的”(就像png一样),以便先前版本的软件可以优雅地跳过它们。
如果您希望在文件中找到规范,则可以考虑嵌入一些提示。想象一下,在png文件中找到字符串http://www.w3.org/TR/PNG/会有多么有帮助。
当然,这完全取决于格式的目的。
一种灵活的方法是将整个文件结构化为TLV(标签-长度-值)三元组。例如,可以使您的文件由记录组成,每个记录以4字节标题开头:
1 byte = record type
3 bytes = record length
followed by record content
关于字节序,如果您在文件中存储字节序指示器,则所有应用程序都必须支持所有字节序格式。另一方面,如果您为文件指定特定的字节序,则仅在具有不匹配字节序的平台上的应用程序需要进行额外的工作,并且可以在编译时决定(使用条件编译)。
在开始之前,了解如何使用您的文件是最重要的。
大多数答案都在可移植性/兼容性方面提供了很好的建议,因此我不会再添加更多。但请考虑以下(通常被忽视的)事项。
zcat | strings
文件并查看其中的内容。要牢记许多事项,并设计一个好的格式需要大量的规划和远见。小细节,例如使用本地整数带来的性能提升或者zcat
文件并获取有用信息,可能会让您的产品获得优势,但是您需要小心,以免为此牺牲重要东西。
为了使文件具有未来的可扩展性,一种方法是提供块。在文件头数据之后,可以开始第一个块。该块可以有一个字节或者单词代码来表示块类型,然后是以字节为单位的大小。现在您可以任意添加新的块类型,并且可以跳过块的末尾。
而对于指针数组,无论在哪里都是O(1)时间:你只需要一个索引号,就可以检索和跟随指针来获取你的数据。
当使用这种方法编写文件时,你当然需要在进行任何写入之前在内存中构建起表格。
magic number/file id
version
tag for first data entry
pointer to first data entry --------+
tag for second data entry |
pointer to second data entry |
... |
length of first data entry <--------+
value for first data entry
...