如何将一个(大端)变长的二进制字节数组转换为(无符号)整数/长整数?例如,'\x11\x34'
表示 4404。
我目前正在使用:
def bytes_to_int(bytes):
return int(bytes.encode('hex'), 16)
这种方法虽然简短易懂,但可能不太高效。是否有更好的(更明显的)方法?
Python传统上不太使用C语言中的“大端格式数”,因为它们对于C来说太大了。(如果你正在处理2字节、4字节或8字节的数字,则struct.unpack
是答案。)
但足够多的人厌倦了没有一个明显的方法来做到这一点,因此Python 3.2添加了一个方法int.from_bytes
,它正是你想要的:
int.from_bytes(b, byteorder='big', signed=False)
很不幸,如果你使用的是旧版本的Python,你没有这个功能。那么,除了显而易见的选择之外(升级到3.2或更好的3.4),你还有哪些选项?
首先,看看你的代码。我认为 binascii.hexlify
比 .encode('hex')
更好,因为 "encode" 对于字节字符串(而不是 Unicode 字符串)的方法似乎有些奇怪,实际上在 Python 3 中已经被淘汰。但其他方面,它对我来说似乎相当易读和明显。并且它应该非常快 - 是的,它必须创建一个中间字符串,但它正在 C 中执行所有循环和算术运算(至少在 CPython 中),这通常比在 Python 中快一个或两个数量级。除非你的bytearray
非常大,分配字符串本身将是昂贵的,否则我不会担心性能问题。
或者,你可以用循环来完成。但那会变得更冗长,并且至少在 CPython 中要慢得多。
你可以尝试消除显式循环以获得隐式循环,但显然要做到这一点的函数是reduce
,这被部分社区认为不符合 Python 的风格,并且当然需要为每个字节调用函数。
你可以展开循环或reduce
,通过将它们分解成 8 个字节的块并循环访问 struct.unpack_from
,或者只需执行一个大的struct.unpack('Q'*len(b)//8 + 'B' * len(b)%8)
并循环访问它,但这会使它变得不那么易读,而且可能并没有快多少。
你可以使用 NumPy… 但如果你的数据比 64 或者也许是 128 位还要大,它最终会将所有东西都转换为 Python 对象。
因此,我认为你的答案是最好的选择。
下面是一些时间比较,将其与最明显的手动转换进行比较:
import binascii
import functools
import numpy as np
def hexint(b):
return int(binascii.hexlify(b), 16)
def loop1(b):
def f(x, y): return (x<<8)|y
return functools.reduce(f, b, 0)
def loop2(b):
x = 0
for c in b:
x <<= 8
x |= c
return x
def numpily(b):
n = np.array(list(b))
p = 1 << np.arange(len(b)-1, -1, -1, dtype=object)
return np.sum(n * p)
In [226]: b = bytearray(range(256))
In [227]: %timeit hexint(b)
1000000 loops, best of 3: 1.8 µs per loop
In [228]: %timeit loop1(b)
10000 loops, best of 3: 57.7 µs per loop
In [229]: %timeit loop2(b)
10000 loops, best of 3: 46.4 µs per loop
In [283]: %timeit numpily(b)
10000 loops, best of 3: 88.5 µs per loop
在Python 3.4中进行比较:
In [17]: %timeit hexint(b)
1000000 loops, best of 3: 1.69 µs per loop
In [17]: %timeit int.from_bytes(b, byteorder='big', signed=False)
1000000 loops, best of 3: 1.42 µs per loop
所以,你的方法仍然非常快...
你需要的功能可以使用struct.unpack(...)函数实现。
'\x1134'
是什么?你是指'\x11\x34'
吗?还是'\\x1134'
?因为你写的是一个由三个字节组成的字节串,其中包含了0x11、0x33和0x34字节,我认为这不是你所拥有或想要的。 - abarnert