我有一个模拟读取我们创建的大型二进制数据文件(10到100的GB)。出于速度原因,我们使用二进制这些文件是系统相关的,从我们运行的每个系统上的文本文件转换而来,所以我不关心可移植性。这些文件当前是POD结构的许多实例,用fwrite编写。
我需要更改结构,所以我想添加一个包含文件版本号的标头,它会随着结构的变化而增加。由于我这样做,我想添加一些其他信息。我正在考虑结构的大小,字节顺序,以及创建二进制文件的代码的svn版本号。还有其他什么东西可以添加吗?
答案 0 :(得分:13)
根据我的经验,对你需要的数据进行二次猜测总是浪费时间。重要的是以可扩展的方式构建元数据。对于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)。阅读页脚,并验证magicString。然后,根据metadataFooterSize回顾并读取元数据。根据文件中包含的页脚大小,您可以使用缺少字段的默认值。
正如KeithB指出的那样,您甚至可以使用这种技术将元数据存储为XML字符串,从而提供完全可扩展的元数据的优势,以及二进制数据的紧凑性和速度。
答案 1 :(得分:7)
对于大型二进制文件,我会认真看待HDF5(Google for it)。即使它不是你想要采用的东西,它也可能会指出你在设计自己的格式时有一些有用的方向。
答案 2 :(得分:4)
对于大型二进制文件,除了版本号之外,我倾向于记录计数和CRC,原因是大型二进制文件随着时间的推移或在传输过程中比较小的二进制文件更容易被截断和/或损坏。我最近惊恐地发现Windows根本不能处理这个问题,因为我使用资源管理器将几百个文件中的2TB复制到连接的NAS设备上,并发现每个副本上有2-3个文件被损坏(不完全复制)。
答案 3 :(得分:3)
如果稍后将其他结构写入二进制文件,则该文件类型的标识符将非常有用。 也许这可能是一个短字符串,所以你可以通过查看文件(通过十六进制编辑器)看到它包含的内容。
答案 4 :(得分:3)
如果它们那么大,我会在文件开头保留一个健康的空间(64K?)空间,并将元数据放在XML格式中,然后是文件结尾字符(Ctrl-Z)对于DOS / Windows,ctrl-D是否为unix?)。这样,您就可以使用各种适用于XML的工具集轻松地检查和解析元数据。
否则我会选择其他人已经说过的内容:文件创建的时间戳,创建它的机器的标识符,基本上您可以考虑用于诊断目的的任何其他内容。理想情况下,您将包含结构格式本身的定义。如果您经常更改结构,那么维护适当版本的代码以阅读各种格式的旧数据文件会非常困难。
@highpercomp提到的HDF5的一大优势是,您只需要担心结构格式的变化,只要您对名称和数据类型有一些约定即可。结构名称和数据类型都存储在文件本身中,因此您可以将C代码吹到smithereens并且无关紧要,您仍然可以从HDF5文件中检索数据。它让你不用担心数据的格式以及更多关于结构的数据,即我不关心字节序列,这是HDF5的问题,但我关心字段名称等。
我喜欢HDF5的另一个原因是你可以选择使用压缩,这需要花费很少的时间,如果数据正在缓慢变化或者大部分相同,除了一些错误的blip之外,你可以在存储空间中获得巨大的胜利有趣的。
答案 5 :(得分:2)
@rstevens说'文件类型的标识符'...声音建议。传统上,这被称为幻数,并且在文件中,不是滥用的术语(与代码不同,它是滥用的术语)。基本上,它是一些数字 - 通常至少4个字节,我通常确保这些字节中至少有一个不是ASCII - 您可以使用它来验证文件是否是您期望的类型,并且混淆的可能性很小。您还可以在/ etc / magic(或本地等效文件)中编写规则,以报告包含幻数的文件是您的特殊文件类型。
您应该包含文件格式版本号。但是,我建议不要使用代码的SVN号码。当文件格式没有时,您的代码可能会发生变化。
答案 6 :(得分:1)
除了架构版本控制所需的任何信息之外,如果要解决问题,请添加可能有价值的详细信息。例如:
我们发现这非常有用(a)获取我们原本不得不要求客户提供的信息以及(b)获取正确的信息 - 令人惊讶的是有多少客户报告他们运行的是不同版本的软件数据所声称的内容!
答案 7 :(得分:1)
您可以考虑将文件偏移量放在标题中的固定位置,这会告诉您实际数据在文件中的开始位置。这样可以在需要时更改标题的大小。
在一些情况下,我将值0x12345678放入标题中,这样我就可以检测文件格式是否与正在处理它的机器的字节顺序相匹配。
答案 8 :(得分:1)
由于我在电信设备配置和固件升级方面的经验表明,您只需要在开始时(这很重要)从版本(标头的固定部分)开始实际需要几个预定义字节。其余标题是可选的,通过指示正确的版本,您可以始终显示如何处理它。这里重要的是你最好在文件末尾放置标题的'变量'部分。如果您在标头上计划操作而不修改文件内容本身。这也简化了“追加”操作,应该重新计算变量头部分。
很高兴有固定大小标题的功能(在开头):
好的,对于变量部分XML或标题中的一些漂亮的可扩展格式是个好主意,但它真的需要吗?我在ASN编码方面有很多经验......在大多数情况下,它的用法都是过度的。
好吧,也许当你看到RFC 2126(第4.3章)中描述的TPKT格式等事情时,你会有额外的理解。
答案 9 :(得分:0)
如果要在标题中添加版本号,则可以在需要更改POD结构或向标题添加新字段时随时更改该版本。
所以现在不要在标题中添加内容,因为它可能很有趣。您只需创建必须维护的代码,但这些代码几乎没有实际价值。
答案 10 :(得分:0)
对于大型文件,您可能希望添加数据定义,因此您的文件格式会自我描述。
答案 11 :(得分:0)
我的变化结合了Roddy和Jason S的方法。
总结 - 将格式化文本元数据放在文件末尾,以确定其长度存储在别处。
1)在文件的开头放置一个长度字段,以便在结束时知道元数据的长度,而不是假定固定的长度。这样,要获取元数据,您只需读取固定长度的初始字段,然后从文件末尾获取元数据blob。
2)使用XML或YAML或JSON作为元数据。如果最后附加元数据,这是特别有用/安全的,因为没有人阅读文件会自动认为它只是因为它以XML开头而是所有XML。
这种方法的唯一缺点是当你的元数据增长时,你必须同时更新文件的头部和尾部,但是其他部分可能仍然会被更新。如果它只是像上次访问日期一样更新琐事,那么元数据长度不会改变,所以它只需要就地更新。