二进制文件和跨平台兼容性

问题描述:

我写了一个C++库,将我的数据(定制结构集合等)保存到二进制文件中。我目前在我的Windows(XP)机器上使用(即创建消耗)本地文件。为了简单起见,我们可以考虑将库分为两部分:一个写入器(创建文件)和一个读取器消费者(简单地从文件读取数据)。二进制文件和跨平台兼容性

但是最近,我想也消耗我的Linux机器上(即读)的数据我的XP机器上的文件我已经创建。我必须指出,在这个阶段,这两台机器都是PC(因此具有相同的亲和力等)。

因为我是图书馆的创建者,所以我可以构建一个阅读器(并为Linux编译[Ubuntu 9.10])。我的问题,我才走上这条路(建设读卡器等)是:

假设我已经成功地建立了Linux的读者,

我可以简单地复制防空火炮,这是文件的创建 Windows(XP)机器到Linux(Ubuntu 9.10)机器并使用Linux读取器成功读取复制的过载文件?

+0

您必须更多地定义“二进制文件”的含义。你使用'fwrite'来写'struct'等数据吗?你是否将所有内容分解为字节然后写入数据? – 2009-12-21 16:14:19

+1

什么是所有的SHOUTING?你可以使用星号**来强调**,它的可读性更强...... – danio 2009-12-22 11:00:28

+0

@danio他来自DOS,所有东西都是CAPITAL – zeitue 2015-08-17 03:32:19

对于文件二进制兼容:

  • 字节顺序必须匹配(因为它为你)
  • 位域打包顺序必须是相同的
  • 尺寸和符号性的类型必须相同
  • 编译器必须对填充和对齐

同样决定这当然是可能的˚F或者所有这些条件都得到满足,或者你不会碰到任何他们没有的情况。不过,至少,我会添加一些理智检查和/或哨兵成员来检测问题。

+0

嗨moonshadow,感谢您的反馈。你能详细说明一些吗 - 当你有时间的时候,用一个简单的类包含一个std :: vector 我可以毫不含糊地理解你的意思,理智检查和/或哨兵成员。我能想到的实现这些检查的唯一方法是使用limits.h中定义的常量 - 这是你的意思吗? - 或者你有更优雅的方法? – 2009-12-21 16:29:46

+0

另外,我不知道如何检查需求2,3和4(您上面列出的)是否成立。我在XP上使用VS2008构建,并在Ubuntu上使用gcc 4.4.1 - 关于如何检查这些要求是否被违反的任何提示? – 2009-12-21 16:32:04

+2

@贴吧:“哨兵成员”,我的意思是安排写入文件的顶层结构包含一个已知常数值的成员,并在文件末尾放置一个成员;在加载时,检查这些成员是否包含您所期望的值 - 这应该会遇到编译器之间size/padding不同的问题。 – moonshadow 2009-12-21 16:43:05

二进制文件应该在具有相同字节的机器上兼容。

您的代码中可能存在的问题是整数的大小,您不一定假定不同操作系统上的编译器具有相同的大小int。所以,无论是复制的字节块和扮演他们,或使用INT16,INT32等

如果:

  • 机器具有相同的字节顺序(如你说他们有)和
  • 你打开以二进制模式流,因为文本模式可能会做一些有趣的事情与线两端和
  • 你干净编程,这样你就不会绊倒实现定义的东西,如比对,数据类型大小和结构包装,

然后是,你的文件应该是便携式的。

第三个要点是使文件格式成为“便携”文件格式的原因。根据结构中的数据类型,它可能非常容易或有点棘手。位域或从不同类型重新解释的数据特别棘手。

你可以考虑看看Boost Serialization Library。 已经有很多想法,它将为您处理许多潜在的跨平台不兼容问题。 当然,对于您的特殊使用情况来说,这可能是过度的,特别是如果您已经实现了读写器&。

结构是不是的文件格式,你不应该尝试像这样使用它们。

当试图使freadfwrite的结构工作时,有大量的黑客使其工作。您可以使用字节交换整数,以便在小端和大端机器之间共享文件。您将结构更改为使用固定宽度的整数类型,以便您可以在不同字大小的机器(例如x86和x64机器之间)之间共享。您可以添加编译器特定的编译指示来控制结构的填充以在编译器版本之间共享。

它有效,但它很丑。更何况,容易出错。

很像The byte order fallacy中的建议,更好的主意是编写代码来单独读取/写入字段。通过编写自己的代码,可以确保没有填充,并且可以独立于整数的本地大小选择整数大小,并且可以在不进行字节交换的情况下(通过分别读取/写入整数字节)来支持两个字节序。

与hacky方法不同,这很难弄错。此外,由于您不依赖任何编译器或特定架构的行为,您的代码可以在所有编译器和体系结构上运行,或者不运行。如果你做得对,你不应该有任何平台特定的错误。

有一个缺点;单独读取/写入字段将比直接使用fread/fwrite慢。您可以设置一个缓冲区(uint8_t buffer[])并将整个数据写入其中,然后立即写入所有内容,这可能会有所帮助,但仍然会变慢(因为您仍然必须将字段移入一次一个缓冲区),但对于大多数目的来说,它仍然足够快(例外是嵌入式/实时系统或极高性能计算)。