如何将一个27字节的数组转换为数字等价物,其中每3个字节是2的补码数?

时间:2016-04-13 23:15:49

标签: python bit-manipulation twos-complement

我想解压缩一个包含9个数字的数据包,每个数字都是2个补码格式,包含3个字节。因此我的包中有27个字节。 我有这个,这允许我使用struct.unpack 4字节数字,但我的数字乘以16。

工作但输出乘以16

data = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

a = a[:3] + [0] + a[3:6] + [0] + a[6:9] + [0] + a[9:12] + [0] + a[12:15] + [0] + a[15:18] + a[18:21] + [0] + [0] + a[21:24] + [0] + a[24:27] + [0]

ch0, ch1, ch2, ch3, ch4, ch5, ch6, ch7, ch8 = struct.unpack('>lllllllll', bytearray(a))

对于4个字节的2的补码,我必须在负数的前面添加0xFF,在正数的前面添加0x00。

有没有聪明的方法来执行该操作?最有效的方法是什么?

3 个答案:

答案 0 :(得分:2)

您可以使用辅助函数来扩展第三个字节的符号位,使其成为第四个字节:

import struct

data = [0x00, 0x00, 0x00,
        0xff, 0xff, 0xff,
        0x00, 0x00, 0x02,
        0xff, 0xff, 0xfd,
        0x00, 0x00, 0x04,
        0xff, 0xff, 0xfb,
        0x00, 0x00, 0x06,
        0xff, 0xff, 0xf9,
        0x00, 0x00, 0x08]

signed = lambda v: [0xff if v & 0x80 else 0x00,]
ch = [struct.unpack('>i', ''.join(map(chr, signed(data[i]) + data[i:i+3])))[0]
                                                    for i in range(0, len(data), 3)]

print(ch)  # -> [0, -1, 2, -3, 4, -5, 6, -7, 8]

答案 1 :(得分:1)

我建议将3字节值解析为单个带符号字节和无符号(16位)短路。然后,您可以使用数学运算符将它们重新组合成单​​个数字。

这是一个列表理解,可以一步完成(给定s作为bytearraybytes字符串):

[(a<<16) + b for a, b in zip(*([iter(struct.unpack('>'+'bH'*9, s))]*2))]

最里面的部分使用struct.unpack进行上述解析。然后我在相同迭代器的两个副本上使用zip来获取字节/短对(这是一个很好的技巧!),并在将字节加到一起之前将字节左移16位。

如果你已经将你的字节作为无符号整数,你甚至不需要使用struct.unpack,你可以直接将它们组合成三个一组(只需要一点点逻辑就可以使符号正确第一个字节):

[((a if a < 128 else a-256)<<16) + (b<<8) + c for a, b, c in zip(*([iter(data)]*3))]

答案 2 :(得分:1)

如果您使用的是Python 3,请使用类方法int.from_bytes。它可以处理任何长度的字节簇:

#!python3
data = bytes([0,0,0,0xff,0xff,0xff,0,0,1,0xff,0xff,0xfe,0,0,3,0xff,0xff,0xfd,0,0,4])
for i in range(0,len(data),3):
    print(int.from_bytes(data[i:i+3],'big',signed=True))

输出:

0
-1
1
-2
3
-3
4

还有to_bytes,如果您还需要生成它们:

>>> (-2).to_bytes(3,'big',signed=True)
b'\xff\xff\xfe'