【问题标题】:Convert binary to signed, little endian 16bit integer in Python在 Python 中将二进制转换为有符号的小端 16 位整数
【发布时间】:2021-03-09 08:09:07
【问题描述】:

尝试将二进制列表转换为有符号的 16 位小端整数

input_data = [['1100110111111011','1101111011111111','0010101000000011'],['1100111111111011','1101100111111111','0010110100000011']]
Desired Output =[[-1074, -34, 810],[-1703, -39, 813]]

这是我到目前为止所得到的。改编自:Hex string to signed int in Python 3.2?, Conversion from HEX to SIGNED DEC in python

results = []
for i in input_data:
   hex_convert = [hex(int(x,2)) for x in i]
   convert = [int(y[4:6] + y[2:4], 16) for y in hex_convert]
   results.append(convert)
print (results)
output: [[64461, 65502, 810], [64463, 65497, 813]]

这很好用,但上面是无符号整数。我需要能够处理负值的有符号整数。然后我尝试了另一种方法:

results_2 = []
for i in input_data:
   hex_convert = [hex(int(x,2)) for x in i]
   to_bytes = [bytes(j, 'utf-8') for j in hex_convert]
   split_bits = [int(k, 16) for k in to_bytes]
   convert_2 = [int.from_bytes(b, byteorder = 'little', signed = True) for b in to_bytes]
   results_2.append(convert_2)
print (results_2)
Output: [[108191910426672, 112589973780528, 56282882144304], [108191943981104, 112589235583024, 56282932475952]]

这个结果比第一个更疯狂。我知道我的方法是错误的,而且我一直无法理解二进制转换等并没有帮助,但我觉得我走在正确的道路上:

(b, byteorder = 'little', signed = True)

但无法找出我错在哪里。任何解释此概念的帮助将不胜感激。

【问题讨论】:

  • 是的,也不起作用,它输出: [[52731, 57087, 10755], [53243, 55807, 11523]] 不是 [[-1074, -34, 810],[- 1703, -39, 813]]
  • 你的输入是二进制还是别的?
  • 它是一个(列表的)列表中的 16 位二进制,如上所示
  • 您想要的输出似乎有错别字?我手动计算[[-1075, -34, 810], [-1073, -39, 813]],这也是我的代码给出的。

标签: python binary hex endianness


【解决方案1】:

这个结果比第一个更疯狂。我知道我的方法是错误的......但无法弄清楚我错在哪里。

问题在于转换字节。让我们一步一步看:

int(x, 2)

很好;我们将字符串视为整数值的 base-2 表示,并获取该整数。唯一的问题是它是 a) 无符号和 b) 大端。

hex(int(x,2))

它的作用是创建一个整数的字符串表示形式,以 16 为基数,带有 0x 前缀。值得注意的是,我们需要每个字节有两个文本字符。这已经走错路了。

您可能已经考虑过使用十六进制,因为您在字符串表示中看到了\xAB 样式转义。这是完全不同的事情。字符串'\xAB' 包含一个字符。字符串'0xAB' 包含四个。

从那里开始,其他一切仍然是无稽之谈。使用文本编码转换为 bytes 仅意味着文本字符 0 被替换为字节值 48(因为在 UTF-8 中,它使用具有该值的单个字节进行编码)。对于这些数据,我们使用 UTF-8 得到的结果与假设纯 ASCII 相同(因为 UTF-8 是“ASCII 透明的”并且文本中没有非 ASCII 字符)。


那么我们该怎么做呢?

我们希望将第一步中的整数转换为用于表示它的字节。正如.from_bytes 类方法允许我们从底层字节创建整数一样,也有一个实例方法允许我们获取表示整数的字节。

因此,我们使用.to_bytes,指定当我们从二进制字符串创建int 时假定的长度、符号和字节序——这为我们提供了对应于该字符串的字节。然后,我们从这些字节重新创建整数,除了现在指定正确的符号和字节序。 .to_bytes 让我们指定长度的原因是因为整数没有特定的长度 - 表示它需要最少的字节数,但您可以使用任意多的字节数。 (如果您想处理有符号值,这一点尤其重要,因为它会自动进行符号扩展。)

因此:

for i in input_data:
    values = [int(x,2) for x in i]
    as_bytes = [x.to_bytes(2, byteorder='big', signed=False) for x in values]
    reinterpreted = [int.from_bytes(x, byteorder='little', signed=True) for x in as_bytes]
    results_2.append(reinterpreted)

但是让我们稍微改进一下代码的组织。我将首先创建一个函数来处理单个整数值,然后我们可以使用推导来处理列表。事实上,我们可以对嵌套列表使用嵌套推导。

def as_signed_little(binary_str):
    # This time, taking advantage of positional args and default values.
    as_bytes = int(binary_str, 2).to_bytes(2, 'big')
    return int.from_bytes(as_bytes, 'little', signed=True)

# And now we can do:
results_2 = [[as_signed_little(x) for x in i] for i in input_data]

【讨论】:

  • 哇!感谢您的详细解释,这对我来说更有意义。不过我确实有几个问题: 1. 为什么我们需要在“as_bytes”变量中使用“big”?一切不都是小事吗? 2. 我已将上述内容插入另一个函数(工作正常),但当我只需要值“-1705”时,它输出为“[-1705]”。如果我的示例具有误导性,我们深表歉意。
  • 忽略2。我将列表展平
  • big 是表示原始数据的字节应具有的字节序。 Python 最初将 1100110111111011 读取为 52731,而不是 64461,因为它将 11001101 视为高位字节。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-30
相关资源
最近更新 更多