在 Python 3 中将二进制字符串转换为字节数组

新手上路,请多包涵

尽管有很多相关问题,但我找不到任何符合我问题的问题。我想将二进制字符串(例如 "0110100001101001" )更改为字节数组(同一个示例, b"hi" )。

我试过这个:

 bytes([int(i) for i in "0110100001101001"])

但我得到了:

 b'\x00\x01\x01\x00\x01' #... and so on

在 Python 3 中执行此操作的正确方法是什么?

原文由 Numeri 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.4k
2 个回答

下面是按照 Patrick 提到的第一种方式执行此操作的示例:将位串转换为 int 并一次取 8 位。这样做的自然方法会以相反的顺序生成字节。为了让字节恢复正确的顺序,我在 bytearray 上使用扩展切片表示法,步长为 -1: b[::-1]

 def bitstring_to_bytes(s):
    v = int(s, 2)
    b = bytearray()
    while v:
        b.append(v & 0xff)
        v >>= 8
    return bytes(b[::-1])

s = "0110100001101001"
print(bitstring_to_bytes(s))

显然,帕特里克的第二种方式更为紧凑。 :)

但是,在 Python 3 中有一种更好的方法:使用 int.to_bytes 方法:

 def bitstring_to_bytes(s):
    return int(s, 2).to_bytes((len(s) + 7) // 8, byteorder='big')


如果 len(s) 保证 是8的倍数,那么 .to_bytes 的第一个arg可以简化:

 return int(s, 2).to_bytes(len(s) // 8, byteorder='big')

这将引发 OverflowError 如果 len(s) 不是 8 的倍数,这在某些情况下可能是可取的。


另一种选择是使用双重否定来执行上限除法。对于整数 a 和 b,底除法使用 //

 n = a // b

给出整数 n 使得

n <= a/b < n + 1

例如,

47 // 10 给出4,并且

-47 // 10 给出-5。所以

-(-47 // 10) 给出 5,有效地执行上限划分。

因此在 bitstring_to_bytes 我们 可以 这样做:

 return int(s, 2).to_bytes(-(-len(s) // 8), byteorder='big')

然而,没有多少人熟悉这种高效紧凑的习语,因此通常认为它的可读性不如

return int(s, 2).to_bytes((len(s) + 7) // 8, byteorder='big')

原文由 PM 2Ring 发布,翻译遵循 CC BY-SA 4.0 许可协议

您必须将其转换为 int 并一次取 8 位,或者将其切成 8 字节长的字符串,然后将它们中的每一个转换为 int。在 Python 3 中,正如 PM 2Ring 和 JF Sebastian 的回答所示,— 的 to_bytes() 方法 int 允许您非常有效地执行第一种方法。这在 Python 2 中不可用,因此对于坚持使用该方法的人来说,第二种方法可能更有效。这是一个例子:

 >>> s = "0110100001101001"
>>> bytes(int(s[i : i + 8], 2) for i in range(0, len(s), 8))
b'hi'

为了分解它,范围语句从索引 0 开始,并为我们提供源字符串的索引,但一次前进 8 个索引。由于 s 的长度为 16 个字符,它将为我们提供两个索引:

 >>> list(range(0, 50, 8))
[0, 8, 16, 24, 32, 40, 48]
>>> list(range(0, len(s), 8))
[0, 8]

(我们在这里使用 list() 来显示将从 Python 3 中的范围迭代器检索的值。)

然后,我们可以以此为基础,通过将字符串分成 8 个字符长的片段来将字符串分开:

 >>> [s[i : i + 8] for i in range(0, len(s), 8)]
['01101000', '01101001']

然后我们可以将它们中的每一个转换为以 2 为底的整数:

 >>> list(int(s[i : i + 8], 2) for i in range(0, len(s), 8))
[104, 105]

最后,我们将整个内容包装在 bytes() 中以获得答案:

 >>> bytes(int(s[i : i + 8], 2) for i in range(0, len(s), 8))
b'hi'

原文由 Patrick Maupin 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题