3

我正在将系统从 python 转换为 c++。我需要能够在 c++ 中执行通常使用 Python 执行的操作struct.unpack(将二进制字符串解释为数值)。对于整数值,我可以使用以下数据类型来实现(某种)工作stdint.h

struct.unpack("i", str) ==> *(int32_t*) str; //str is a char* containing the data

这适用于 little-endian 二进制字符串,但在 big-endian 二进制字符串上失败。基本上,我需要一个等效于>在 struct.unpack 中使用标签的功能:

struct.unpack(">i", str) ==> ???

请注意,如果有更好的方法可以做到这一点,我会全力以赴。但是,我不能使用 c++11,也不能使用 Boost 以外的任何第三方库。我还需要能够解释浮点数和双精度数,例如struct.unpack(">f", str)and struct.unpack(">d", str),但是当我解决这个问题时我会解决这个问题。

注意我应该指出我的机器的字节顺序在这种情况下是无关紧要的。我知道我在代码中收到的比特流总是大端的,这就是为什么我需要一个始终涵盖大端情况的解决方案。BoBTFish 在评论中指出的文章似乎提供了一个解决方案。

4

5 回答 5

7

对于 32 位和 16 位值:

这正是大端网络数据的问题。您可以使用ntohl将 32 位转换为主机顺序,在您的情况下为 little-endian。

ntohl() 函数将无符号整数 netlong 从网络字节顺序转换为主机字节顺序。

int res = ntohl(*((int32_t) str)));

这也将处理您的主机是大端并且不会做任何事情的情况。

对于 64 位值

在 linux/BSD 上非标准,您可以看看C++ 中的 64 位 ntohl()?,它指向htobe64

这些函数将整数值的字节编码从当前 CPU(“主机”)使用的字节顺序转换为 little-endian 和 big-endian 字节顺序。

对于 Windows 尝试:如何在 C++ 中的大端和小端值之间进行转换?

它指向_byteswap_uint64以及 16 位和 32 位解决方案以及特定于 gcc 的 __builtin_bswap(32/64) 调用。

其他尺寸

大多数系统没有非 16/32/64 位长的值。那时我可能会尝试将其存储为 64 位值,将其移位并进行翻译。我会写一些很好的测试。我怀疑这是一种不常见的情况,更多细节会有所帮助。

于 2012-12-13T16:31:04.833 回答
4

一次一个字节地解压字符串。

unsigned char *str;
unsigned int result;

result =  *str++ << 24;
result |= *str++ << 16;
result |= *str++ << 8;
result |= *str++;
于 2012-12-13T16:20:03.043 回答
2

首先,你正在做的演员:

char *str = ...;
int32_t i = *(int32_t*)str;

由于严格的别名规则导致未定义的行为(除非str用类似的东西初始化int32_t x; char *str = (char*)&x;)。实际上,强制转换可能会导致未对齐的读取,这会导致某些平台上的总线错误(崩溃)和其他平台上的性能下降。

相反,您应该执行以下操作:

int32_t i;
std::memcpy(&i, c, sizeof(i));

有许多函数可以在主机的本机字节顺序和主机独立的顺序之间交换字节:ntoh*(), hton*(), where *is nothing, l, 或s支持的不同类型。由于不同的主机可能具有不同的字节顺序,因此如果您正在读取的数据在所有平台上使用一致的序列化形式,这可能就是您想要使用的。

ntoh(i);

str您还可以在将字节复制到整数之前手动移动字节。

std::swap(str[0],str[3]);
std::swap(str[1],str[2]);
std::memcpy(&i,str,sizeof(i));

或者您可以使用移位和按位运算符手动操作整数的值。

std::memcpy(&i,str,sizeof(i));
i = (i&0xFFFF0000)>>16 | (i&0x0000FFFF)<<16;
i = (i&0xFF00FF00)>>8  | (i&0x00FF00FF)<<8;
于 2012-12-13T16:24:38.973 回答
0

这属于比特旋转的领域。

for (i=0;i<sizeof(struct foo);i++) dst[i] = src[i ^ mask]; 

其中 mask == (sizeof type -1) 如果存储的字节序和本机字节序不同。

使用这种技术,可以将结构转换为位掩码:

 struct foo {
    byte a,b;       //  mask = 0,0
    short e;        //  mask = 1,1
    int g;          //  mask = 3,3,3,3,
    double i;       //  mask = 7,7,7,7,7,7,7,7
 } s; // notice that all units must be aligned according their native size

同样,这些掩码可以用每个符号两位进行编码:(1<<n)-1,这意味着在 64 位机器中,可以将 32 字节大小的结构的必要掩码编码为单个常量(具有 1、2、4 和 8 字节对齐)。

unsigned int mask = 0xffffaa50;  // or zero if the endianness matches
for (i=0;i<16;i++) { 
     dst[i]=src[i ^ ((1<<(mask & 3))-1]; mask>>=2;
}
于 2012-12-13T16:21:50.030 回答
-1

如果您收到的值是真正的字符串(char* 或 std::string),并且您知道它们的格式信息,sscanf() 和 atoi(),那么,真正的 atoi() 将是您的朋友。它们采用格式良好的字符串并根据传入的格式(一种反向 printf)转换它们。

于 2012-12-13T16:28:10.260 回答