我有一个读写二进制文件的程序。文件在同一平台上执行程序之间可以互换,但由于类型,字节序等的大小,在一台机器上生成的文件在另一个平台上可能无效。
我想要一种快速的方法来断言给定文件对于给定体系结构的读取是有效的。 我对创建文件跨架构不感兴趣(实际上该文件是内存映射的结构)。我只想要一种方法来检查文件是在具有相同大小类型的架构上创建的,然后再阅读它。
一个想法是在文件的开头写一个带有常量幻数的结构。这可以阅读和验证。另一种方法是将sizeof
各种类型存储在单字节整数中。
这是针对C的,但我认为这个问题与语言无关,问题与语言相同。
最好的方法是什么?
我欢迎对这个问题的标题进行修改!
答案 0 :(得分:2)
我喜欢文件创意开头的神奇数字。你可以用魔术值进行这些检查:
如果至少有两个魔术字节并将它们视为单个多字节整数,则可以检测字节序更改。例如,如果您选择0xABCD,并且您的代码读取为0xCDAB,那么您所处的平台的字节顺序与文件编写的字节顺序不同。
如果使用4或8字节整数,则可以检测32位与64位平台,如果选择数据类型,则在两个平台上的大小不同。
如果不仅仅是一个整数或您仔细选择它,您可以排除意外读取其他程序写出的文件的可能性。请参阅任何Unixy类型系统上的/ etc / magic以获取要避免的良好值列表。
答案 1 :(得分:1)
#include <stdint.h>
union header {
uint8_t a[8];
uint64_t u;
};
const struct header h = { .u = (sizeof( short ) << 0 )
| (sizeof( int ) << 8 )
| (sizeof( long ) << 16 )
| (sizeof( long long ) << 24 )
| (sizeof( float ) << 32 )
| (sizeof( double ) << 40 )
| (sizeof( long double ) << 48 )
| 0 } ;
这应该足以验证类型大小和字节顺序,除了浮点数对此很难。
如果要验证您的浮点数是否以相同的格式存储在编写器和读取器上,那么您可能希望存储一些常量浮点数(比0,1和-1更有趣)在此标题之后的不同大小,并验证它们是您认为应该是的。
存储具有版本号的实际魔术字符串很可能也是好的,因为这是另一种检查,这是正确的文件格式。
如果您不关心花车或类似的东西,请随意删除它们。我没有包含char,因为它应该总是1个字节。
如果您还存储某些结构的大小,这可能是一个好主意:
struct misalligned {
char c;
uint64_t u;
};
这应该允许您轻松确定生成生成文件的代码的编译器的对齐和填充。如果这是在大多数关心对齐的32位计算机上完成的,那么大小将是96,因为在c和u之间将有3个字节的填充,但如果它是在64位机器上完成的那么它的大小可能是128,具有c和u之间有7个字节的填充。如果这是在AVR上完成的,那么它的大小很可能是9,因为没有填充。
答案 2 :(得分:1)
调用uname(2)
函数(或非POSIX平台上的等效函数)并将sysname
中的machine
和struct utsname
字段写入标题开头的标题中文件。
(除了大小和字节序之外还有更多内容 - 浮点格式和结构填充标准也有所不同。所以你想断言的机器ABI真的是相同的。)
答案 3 :(得分:0)
首先,我完全同意 Warren Young 提供的上一个答案。
这是我们正在谈论的元数据案例。
在文件系统和同类内容上,我更喜欢在二进制文件的开头填充一个(对于结构的大小)元数据。这样可以保留数据结构对齐并简化附加写入。
如果是异构的,我更喜欢在每个数据或数据范围前使用Structure-Value或Structure-Length-Value(也称为Type Length Value)。
在具有随机连接的流上,您可能希望在二进制数据的持续流动期间使用类似HDLC (on Wikipedia)的某种结构同步和元数据重复。如果您熟悉音频/视频格式,您可能会想到数据流中的TAG,这些数据流本质上是由帧组成的。
好主题!