在 Python 3 中将二进制字符串转换为字节数组
Convert binary string to bytearray in Python 3
尽管有很多相关问题,但我找不到符合我问题的问题。我想将二进制字符串(例如,"0110100001101001"
)更改为字节数组(同样的示例,b"hi"
)。
我试过这个:
bytes([int(i) for i in "0110100001101001"])
但我得到了:
b'\x00\x01\x01\x00\x01' #... and so on
在 Python 3 中执行此操作的正确方法是什么?
您必须将其转换为 int 并一次取 8 位,或者将其切成 8 字节长的字符串,然后将它们中的每一个都转换为 int。在 Python 3 中,正如 PM 2Ring 和 J.F Sebastian 的答案所示,int
的 to_bytes()
方法允许您非常有效地执行第一种方法。这在 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 提到的第一种方法进行操作的示例:将位串转换为 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))
显然,帕特里克的第二种方式更紧凑。 :)
但是,Python3 中有更好的方法:使用 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')
如果 len(s)
不是 8 的倍数,这将引发 OverflowError
,这在某些情况下可能是可取的。
另一种选择是使用双重否定来进行上限除法。对于整数 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 (s, 2).to_bytes((len(s) + 7) // 8, byteorder='big')
>>> zero_one_string = "0110100001101001"
>>> int(zero_one_string, 2).to_bytes((len(zero_one_string) + 7) // 8, 'big')
b'hi'
它returns bytes
对象是一个不可变的字节序列。如果你想得到一个 bytearray
——一个可变的字节序列——那么只需调用 bytearray(b'hi')
.
尽管有很多相关问题,但我找不到符合我问题的问题。我想将二进制字符串(例如,"0110100001101001"
)更改为字节数组(同样的示例,b"hi"
)。
我试过这个:
bytes([int(i) for i in "0110100001101001"])
但我得到了:
b'\x00\x01\x01\x00\x01' #... and so on
在 Python 3 中执行此操作的正确方法是什么?
您必须将其转换为 int 并一次取 8 位,或者将其切成 8 字节长的字符串,然后将它们中的每一个都转换为 int。在 Python 3 中,正如 PM 2Ring 和 J.F Sebastian 的答案所示,int
的 to_bytes()
方法允许您非常有效地执行第一种方法。这在 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 提到的第一种方法进行操作的示例:将位串转换为 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))
显然,帕特里克的第二种方式更紧凑。 :)
但是,Python3 中有更好的方法:使用 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')
如果 len(s)
不是 8 的倍数,这将引发 OverflowError
,这在某些情况下可能是可取的。
另一种选择是使用双重否定来进行上限除法。对于整数 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 (s, 2).to_bytes((len(s) + 7) // 8, byteorder='big')
>>> zero_one_string = "0110100001101001"
>>> int(zero_one_string, 2).to_bytes((len(zero_one_string) + 7) // 8, 'big')
b'hi'
它returns bytes
对象是一个不可变的字节序列。如果你想得到一个 bytearray
——一个可变的字节序列——那么只需调用 bytearray(b'hi')
.