【问题标题】:Binary files and cross platform compatibility二进制文件和跨平台兼容性
【发布时间】:2010-12-28 18:58:42
【问题描述】:

我编写了一个 C++ 库,可将我的数据(自定义结构的集合等)保存到二进制文件中。我目前在我的 Windows (XP) 机器上本地使用(即 createconsume)这些文件。为简单起见,让我们将库分为两部分:writer(创建文件)和 readerconsumer(简单地从文件)。

不过,最近,我还想使用(即读取)我在我的 XP 机器上、在我的 Linux 机器上创建的数据文件。在这个阶段我必须指出,两台机器都是 PC(因此具有相同的 endianess 等)。

我可以构建一个阅读器(并为 Linux [确切地说是 Ubuntu 9.10] 编译),因为我是库的创建者。我的问题是,在我踏上这条路(构建阅读器等)之前:

假设我已经成功构建了 Linux 阅读器,

我可以简单地将在 windows (XP) 机器上创建的文件复制到 Linux (Ubuntu 9.10) 机器上,然后使用 Linux 阅读器成功读取复制的文件吗? p>

【问题讨论】:

  • 您必须进一步定义“二进制文件”的含义。您是否使用fwrite 编写struct 数据等内容?你是否将所有内容分解为字节,然后写入数据?
  • 大喊大叫是怎么回事?您可以将星号用于强调,这样更具可读性...
  • @danio 他来自 DOS,所有的东西都是资本

标签: c++ c ubuntu windows-xp filesystems


【解决方案1】:

结构不是一种文件格式,您不应该尝试这样使用它们。

当试图使结构与freadfwrite 一起工作时,有大量的黑客可以让它工作。您可以对整数进行字节交换,以便可以在 little-endian 和 big-endian 机器之间共享文件。您将结构更改为使用固定宽度的整数类型,因此您可以在具有不同字长的机器之间共享(例如在 x86 和 x64 机器之间)。您添加特定于编译器的 pragma 来控制结构的填充以在编译器版本之间共享。

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

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

与 hacky 方法不同,这很难出错。此外,由于您不依赖任何编译器或体系结构特定的行为,您的代码要么适用于所有编译器和体系结构,要么不适用。如果你做得对,你就不应该有任何特定于平台的错误。

有一个缺点;单独读取/写入字段将比直接使用 fread/fwrite 慢。您可以设置一个缓冲区 (uint8_t buffer[]) 并将全部数据写入其中,然后一次将所有内容都写出来,这可能会有所帮助,但它仍然会更慢(因为您仍然需要移动每次一个字段进入缓冲区),但对于大多数用途来说,它仍然足够快(嵌入式/实时系统或极高性能计算除外)。

【讨论】:

    【解决方案2】:

    您可以考虑查看Boost Serialization Library。 它已经考虑了很多,它将为您处理许多潜在的跨平台不兼容问题。 当然,对于您的特定用例来说,这可能是多余的,特别是如果您已经实现了编写器和读取器。

    【讨论】:

      【解决方案3】:

      如果:

      • 机器具有相同的字节序(正如您所说的那样)并且
      • 您确实以二进制模式打开流,因为文本模式可能会做一些有趣的事情,例如带行尾和
      • 您的编程很干净,因此您不会因实现定义的东西(如对齐、数据类型大小和结构打包)而绊倒,

      那么是的,您的文件应该是可移植的。

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

      【讨论】:

        【解决方案4】:

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

        您的代码中可能遇到的问题是整数的大小,您不一定假设不同操作系统上的编译器具有相同的整数大小。所以要么复制字节块并转换它们,要么使用 int16、int32 等。

        【讨论】:

          【解决方案5】:

          对于要二进制兼容的文件:

          • endianness 必须匹配(对您而言)
          • 位域打包顺序必须相同
          • 类型的大小和符号必须相同
          • 编译器必须对填充和对齐做出相同的决定

          当然有可能满足所有这些条件,或者您碰巧没有遇到任何不符合这些条件的情况。不过,至少我会添加一些健全性检查和/或哨兵成员来检测问题。

          【讨论】:

          • 您好月影,感谢您的反馈。您能否详细说明一下 - 当您有时间时,使用一个包含 std::vector 的简单类,我将能够明确地理解您所说的健全性检查和/或哨兵成员的意思。我能想到实现这些检查的唯一方法是使用limits.h中定义的常量——这就是你的意思吗? - 或者你有更优雅的方法?
          • 另外,我不确定如何检查要求 2、3 和 4(您在上面列出的)是否成立。我在 XP 上使用 VS2008 构建,在 Ubuntu 上使用 gcc 4.4.1 - 关于如何检查这些要求没有被违反的任何提示?
          • @Stick it:通过“哨兵成员”,我的意思是安排写入文件的顶级结构包含具有已知常量值的成员,并在末尾放置一个文件;在加载时,检查这些成员是否包含您期望的值 - 这应该会发现编译器之间大小/填充不同的问题。
          • 一个很好的二进制兼容数据库示例是 FoxPro 数据库。它最初旨在兼容多种平台。
          • 天哪 ***。我不明白答案中的任何内容以及问题中的 ENDianness。这些是一些密码词还是一些实际的s***¿¿
          猜你喜欢
          • 1970-01-01
          • 2011-07-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多