从二进制文件读取时将大端转换为小端

Posted

技术标签:

【中文标题】从二进制文件读取时将大端转换为小端【英文标题】:Convert big endian to little endian when reading from a binary file [duplicate] 【发布时间】:2011-04-18 22:40:05 【问题描述】:

我一直在寻找如何将大端转换为小端。但是我没有找到任何可以解决我问题的好方法。似乎有很多方法可以进行这种转换。无论如何,下面的代码在大端系统中工作正常。但是我应该如何编写一个转换函数,以便它也可以在 little-endian 系统上工作呢?

这是一个作业,但它只是一个额外的,因为学校的系统运行大端系统。只是我很好奇,想让它也能在我的家用电脑上运行

#include <iostream>
#include <fstream>

using namespace std;

int main()

   ifstream file;

   file.open("file.bin", ios::in | ios::binary);

   if(!file)
      cerr << "Not able to read" << endl;
   else
   
      cout << "Opened" << endl;

      int i_var;
      double d_var;

      while(!file.eof())
      
         file.read( reinterpret_cast<char*>(&i_var) , sizeof(int) );
         file.read( reinterpret_cast<char*>(&d_var) , sizeof(double) );
         cout << i_var << " " << d_var << endl;
      
   
   return 0;

已解决

所以 Big-endian VS Little-endian 只是字节的相反顺序。无论如何,我编写的这个函数似乎符合我的目的。我在这里添加它以防其他人将来需要它。不过,这仅适用于双精度,对于整数,请使用建议的函数 torak,或者您可以通过使其仅交换 4 个字节来修改此代码。

double swap(double d)

   double a;
   unsigned char *dst = (unsigned char *)&a;
   unsigned char *src = (unsigned char *)&d;

   dst[0] = src[7];
   dst[1] = src[6];
   dst[2] = src[5];
   dst[3] = src[4];
   dst[4] = src[3];
   dst[5] = src[2];
   dst[6] = src[1];
   dst[7] = src[0];

   return a;

【问题讨论】:

即使您解决了字节序问题,浮点格式也可能因平台而异。您不能在一个平台上保存二进制浮点值并期望在另一个平台上加载它们。这是你做这个二进制的要求吗? 这不是必需的,因为作业要在学校的电脑上完成并显示。我只是想知道如何在 Windows 计算机上读取这个二进制文件,这只是我的好奇心。 @sbi - 目前哪些平台在非 IEE-754 中实现了浮点? @Kos: not much,我相信你不需要使用它们。 @Kos Do any real-world CPUs not use IEEE 754? 【参考方案1】:

您可以为您的字节序交换使用一个模板,该模板将针对数据类型进行泛化:

#include <algorithm>

template <class T>
void endswap(T *objp)

  unsigned char *memp = reinterpret_cast<unsigned char*>(objp);
  std::reverse(memp, memp + sizeof(T));

那么你的代码最终会看起来像:

file.read( reinterpret_cast<char*>(&i_var) , sizeof(int) );
endswap( &i_var );
file.read( reinterpret_cast<char*>(&d_var) , sizeof(double) );  
endswap( &d_var );
cout << i_var << " " << d_var << endl;  

【讨论】:

我有类似的东西,但是每个引用都取对象,而不是指针。【参考方案2】:

您可能对ntohl 系列函数感兴趣。这些旨在将数据从网络转换为主机字节顺序。网络字节顺序是大端,因此在大端系统上它们不做任何事情,而在小端系统上编译的相同代码将执行适当的字节交换。

【讨论】:

double 有类似的东西吗? 浮点数的表示方式比 itegers 更复杂(和变化),而且我从未尝试过,所以我不能 100% 确定。但是,假设两台机器上的浮点表示匹配(除了字节序),Bryan 链接到的文章表明它应该是可能的。【参考方案3】:

Linux 提供endian.h,它具有高达 64 位的高效端交换例程。它还自动解释系统的字节顺序。 32 位函数的定义如下:

uint32_t htobe32(uint32_t host_32bits);           // host to big-endian encoding
uint32_t htole32(uint32_t host_32bits);           // host to lil-endian encoding
uint32_t be32toh(uint32_t big_endian_32bits);     // big-endian to host encoding
uint32_t le32toh(uint32_t little_endian_32bits);  // lil-endian to host encoding

对于 16 位和 64 位具有类似名称的函数。 所以你就说

 x = le32toh(x);

将 little-endian 编码的 32 位整数转换为主机 CPU 编码。这对于读取 little-endian 数据很有用。

 x = htole32(x);

将从主机编码转换为 32 位 little-endian。这对于编写 little-endian 数据很有用。

注意在BSD系统上,等效的头文件是sys/endian.h

【讨论】:

还要小心,因为 BSD 头文件是汇编安全的,这意味着您不能将它包含在任何属于您的 C/C++ 程序的汇编文件中。这让我明白了,因为在阅读了 Linux 的 endian.h 标头后,我隐含地假设所有 endian.h 标头都是预处理器,这是错误的。把它放在这里是为了避免其他人落入同一个陷阱。【参考方案4】:

假设您要继续进行下去,保留一些帮助函数的小库文件会很方便。其中 2 个函数应该是 4 字节值和 2 字节值的字节序交换。对于一些可靠的示例(包括代码),请查看this article。

一旦您获得了交换函数,只要您以错误的字节序读取值,就调用相应的交换函数。有时,这里的人们的一个绊脚石是单字节值不需要进行字节序交换,因此,如果您正在读取诸如表示文件中的一串字母的字符流之类的东西,那应该很好。只有当您读取一个值时,这是多个字节(如整数值),您必须交换它们。

【讨论】:

实际上我很困惑,因为有很多不同的交换。在那篇文章中,它是短期、长期和浮动的互换。这些交换中的任何一个都适用于我正在使用的数据类型吗? 解决方案取决于数据值的大小。如果你有一个 2 字节的值,你使用 ShortSwap(),如果你有一个 4 字节的值 LongSwap(). 示例中的FloatSwap() 有点毫无意义,除了浮动存储和长存储之间存在逻辑差异... LongSwap 仍然可以在 4 字节浮点数上正常工作。您在顶部发布的解决方案中所做的实际上与 8 字节值相同。 链接现在是404了! 所以回去吧:web.archive.org/web/20071113102719/http://www.gamedev.net:80/…【参考方案5】:

很高兴补充一点,MS 在 VS 上也支持此功能,请检查以下内联函数:

htond htonf htonl htonll 吨

【讨论】:

不仅在VS中,你还可以在包含上述所有功能的GCC上包含winsock.h

以上是关于从二进制文件读取时将大端转换为小端的主要内容,如果未能解决你的问题,请参考以下文章

将任何变量从大端转换为小端 - 如何避免空指针?

PHP如何将从二进制文件中读取的字节转换为数字

从二进制文件中读取并转换为双精度?

将大端转换为小端的 C/C++ 代码

用mysql原生函数进行字节顺序转换

大小端模式