【问题标题】:Portability for Binary File in C++?C++ 中二进制文件的可移植性?
【发布时间】:2014-01-10 13:56:56
【问题描述】:

对于二进制文件的可移植性,我对二进制 I/O 有疑问。 假设运行我的软件的 PC 使用 8 个字节来存储双变量。 生成的二进制文件将有 8 个字节用于双变量。 现在说文件在使用 6 个字节作为双变量的 PC 中打开(只是假设)。 然后应用程序将只从文件中读取 6 个字节并将其存储在内存中的 double 变量中。 这不仅会导致数据下溢/上溢,而且由于读取不足会产生 2 字节偏移,因此在 double 之后读取的数据肯定会不正确。 我想支持我的应用程序不仅支持 32/64 位,还支持 Windows、Ubuntu PC。 那么如何确保在任何一台 PC 上从同一个文件中读取的数据都是一样的呢?

【问题讨论】:

  • 还有更多的不兼容性,例如字节序!最好是使用文本或其他标准格式。
  • 看序列化/反序列化。
  • 数据太大,无法使用文本文件,通常以千兆字节为单位。阅读在文本中写入这么多双变量需要花费大量时间。因此转向二进制文件。
  • 双精度数是IEEE standard,因此保证它们始终为 64 位。大小在这里不是问题,只是字节序(不太可能遇到的问题)。
  • @Cool_Coder 只有少数类型具有固定大小,可能是因为它们并非源自 C。正如您所见,整数类型和大多数基本类型都有指定的范围进入。然而,浮点数和双精度数是 IEEE 标准,并且具有非常特定的布局和大小,它们永远不应该改变(特别是因为处理器现在通常有它们的寄存器,这遵循标准)。它们是一个例外,但在这种情况下可能对您有用。

标签: c++ file


【解决方案1】:

一般来说,您应该将要存储在二进制文件中的数据包装在您自己的数据结构中,并为这些数据结构实现与平台无关的读/写操作 - 基本上,所有平台写入磁盘的二进制数据结构的大小应该相同(所有支持的平台上基本数据的最大可能大小)。

在数据量较小的平台上写入数据时,数据应额外填充0字节,以确保记录数据的大小保持不变。

读取时,可以在已知大小的固定数据块中读取整个数据,并且应根据写入/正在读取的平台执行转换。这也应该注意字节顺序。您可能希望包含一些指示数据大小的标头,以便在读取时区分记录在不同平台上的文件。

这将为二进制文件提供真正独立于平台的序列化。

双打示例

class CustomDouble
{
public:
double val;
static const int DISK_SIZE;

void toFile(std::ofstream &file)
{
   int bytesWritten(0);
   file.write(reinterpret_cast<const char*>(&val),sizeof(val));
   bytesWritten+=sizeof(val);
   while(bytesWritten<CustomDouble::DISK_SIZE)
   { 
      char byte(0);
      file.write(&byte,sizeof(byte));
      bytesWritten+=sizeof(byte);
   }

}
};
const int CustomDouble::DISK_SIZE = 8;

这确保您总是在您的平台上写入 8 个字节大小的双精度。当您读取文件时,您始终以二进制形式读取这 8 个字节,并在必要时根据写入/正在读取的平台进行转换(您可能会在文件中添加一些小标题以识别它被记录的平台开)

虽然自定义转换确实会增加一些开销,但它比将值存储为文本的开销要少得多,而且通常您只会对不兼容的平台执行转换,而对于相同的平台则不会有任何开销。

【讨论】:

  • 我在我的应用程序中存储双变量的二维数组。因此 I/O 使用标准双变量。所以你的意思是我应该为我的应用程序硬编码双倍的标准尺寸。然后根据环境可以调整文件 I/O 以适应这种硬编码大小?但这会导致数据下溢和溢出。使用二进制文件的重点是避免转换,否则我会对文本文件感到满意。
  • 但是您假设双精度数的最大大小为 8 个字节。这是正确的吗?
  • @Cool_Code 是的,但仅作为示例。它应该是受支持平台的最大可能大小。将 static_assert 也用于比较 sizeof(double) 和 CustomDouble::DISK_SIZE 可能是个好主意,以防止项目在具有更大 double 大小的平台上编译。
【解决方案2】:

cstdint 包含固定大小的类型定义,因此int32_t 将始终为 4 个字节长。当类型的大小对您很重要时,您可以使用它们来代替常规类型。

【讨论】:

  • 我使用双变量来存储数据。有什么解决办法吗?
  • 双打有一个固定的大小,所以你已经在这样做了(本质上)。对于任何其他类型,指定大小会有所帮助。
【解决方案3】:

使用Google Protocol Buffers 或任何其他跨平台序列化库。您也可以推出自己的解决方案,基于 char 保证为 1 个字节(即将任何内容序列化为 char 数组)这一事实。

【讨论】:

  • char 不保证 1 字节恕我直言。 char 是衡量所有其他数据类型大小的标准,即 sizeof double = 8 * sizeof char。 char 可能 != 1 个字节。
  • Google Protocal Buffers 究竟是如何回答我的问题的?我不认为那是我要问的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-29
  • 2016-02-25
  • 1970-01-01
  • 2011-06-11
  • 2015-09-11
相关资源
最近更新 更多