如何将 27 个字节的数组(其中每 3 个字节是一个 2 的补码)转换成它的等效数字?

How to convert an array of 27 bytes where each 3 bytes is a 2's complement number into its numeric equivalent?

我要解压一个数据包,里面有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 字节值解析为单个有符号字节和无符号(16 位)短字符。然后,您可以使用数学运算符将它们重新组合成一个数字。

这是一个列表推导式,一步完成(给定 s 作为 bytearraybytes 字符串):

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

最里面的部分使用 struct.unpack 进行如上所述的解析。然后我在同一迭代器的两个副本上使用 zip 来获得 byte/short 对(这是一个绝妙的技巧!),并在将它们相加之前将字节左移 16 位。

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

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

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

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]

如果您正在使用Python3,请使用class方法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'