如何将字节串转换为整型?

如何在 python 中将字节串转换为 int?

像这样说: 'y\xcc\xa6\xbb'

我想出了一个聪明/愚蠢的方法:

sum(ord(c) << (i * 8) for i, c in enumerate('y\xcc\xa6\xbb'[::-1]))

我知道必须有一些内置的或在标准库中,这样做更简单..。

这与可以使用 int (xxx,16)的 转换十六进制数字字符串不同,但是我想转换实际字节值的字符串。

更新:

我更喜欢 James 的回答,因为它不需要导入另一个模块,但是 Greg 的方法更快:

>>> from timeit import Timer
>>> Timer('struct.unpack("<L", "y\xcc\xa6\xbb")[0]', 'import struct').timeit()
0.36242198944091797
>>> Timer("int('y\xcc\xa6\xbb'.encode('hex'), 16)").timeit()
1.1432669162750244

我的方法是:

>>> Timer("sum(ord(c) << (i * 8) for i, c in enumerate('y\xcc\xa6\xbb'[::-1]))").timeit()
2.8819329738616943

更新:

有人在评论中问导入另一个模块有什么问题。嗯,进口一个模块不一定便宜,看看:

>>> Timer("""import struct\nstruct.unpack(">L", "y\xcc\xa6\xbb")[0]""").timeit()
0.98822188377380371

包括导入模块的成本几乎否定了这种方法所具有的所有优点。我相信这将只包括在整个基准测试运行期间导入它一次的费用; 看看每次我强制它重新加载时会发生什么:

>>> Timer("""reload(struct)\nstruct.unpack(">L", "y\xcc\xa6\xbb")[0]""", 'import struct').timeit()
68.474128007888794

不用说,如果每次导入都要执行大量此方法,那么这个问题就会相应减少。它也可能是输入/输出成本而不是 CPU,因此它可能取决于特定机器的容量和负载特性。

338157 次浏览

您还可以使用 结构模块完成以下工作:

>>> struct.unpack("<L", "y\xcc\xa6\xbb")[0]
3148270713L

正如 Greg 所说,如果处理的是二进制值,那么可以使用 struct,但是如果只有一个“十六进制数”,而且是字节格式的,那么可以像下面这样进行转换:

s = 'y\xcc\xa6\xbb'
num = int(s.encode('hex'), 16)

... 这和..:

num = struct.unpack(">L", s)[0]

除了它可以处理任何字节。

import array
integerValue = array.array("I", 'y\xcc\xa6\xbb')[0]

警告: 以上内容是特定于平台的。字符串-> int 转换的“ I”说明符和 endianness 都取决于您的特定 Python 实现。但是,如果希望一次转换多个整数/字符串,那么数组模块可以快速完成。

在 Python 3.2及更高版本中,使用

>>> int.from_bytes(b'y\xcc\xa6\xbb', byteorder='big')
2043455163

或者

>>> int.from_bytes(b'y\xcc\xa6\xbb', byteorder='little')
3148270713

根据字节串的 Endianness

这也适用于任意长度的 bytestring 整数,以及指定 signed=True的两个补符号整数。

我使用以下函数在 int、十六进制和字节之间转换数据。

def bytes2int(str):
return int(str.encode('hex'), 16)


def bytes2hex(str):
return '0x'+str.encode('hex')


def int2bytes(i):
h = int2hex(i)
return hex2bytes(h)


def int2hex(i):
return hex(i)


def hex2int(h):
if len(h) > 1 and h[0:2] == '0x':
h = h[2:]


if len(h) % 2:
h = "0" + h


return int(h, 16)


def hex2bytes(h):
if len(h) > 1 and h[0:2] == '0x':
h = h[2:]


if len(h) % 2:
h = "0" + h


return h.decode('hex')

资料来源: http://opentechnotes.blogspot.com.au/2014/04/convert-values-to-from-integer-hex.html

在 Python 2.x 中,可以使用格式说明符 <B表示无符号字节,使用 <b表示有符号字节,使用 struct.unpack/struct.pack表示有符号字节。

例如:

x = '\xff\x10\x11'

data_ints = struct.unpack('<' + 'B'*len(x), x) # [255, 16, 17]

还有:

data_bytes = struct.pack('<' + 'B'*len(data_ints), *data_ints) # '\xff\x10\x11'

那个 *是必需的!

有关格式说明符的列表,请参见 < a href = “ https://docs.python.org/2/library/struct.html # format-ters”rel = “ nofollow norefrer”> https://docs.python.org/2/library/struct.html#format-characters

我一直在努力寻找在 Python 2.x 下工作的任意长度字节序列的解决方案。最后,我写了这个,它有点古怪,因为它执行字符串转换,但它工作。

函数用于 Python 2.x,任意长度

def signedbytes(data):
"""Convert a bytearray into an integer, considering the first bit as
sign. The data must be big-endian."""
negative = data[0] & 0x80 > 0


if negative:
inverted = bytearray(~d % 256 for d in data)
return -signedbytes(inverted) - 1


encoded = str(data).encode('hex')
return int(encoded, 16)

这一职能有两个要求:

  • 输入 data需要是 bytearray。你可以这样调用函数:

    s = 'y\xcc\xa6\xbb'
    n = signedbytes(s)
    
  • The data needs to be big-endian. In case you have a little-endian value, you should reverse it first:

    n = signedbytes(s[::-1])
    

Of course, this should be used only if arbitrary length is needed. Otherwise, stick with more standard ways (e.g. struct).

如果处于 version > = 3.2,int.from _ bytes 是最佳解决方案。 “ struct.unpack”解决方案需要一个字符串,因此它不会应用于字节数组。 还有一个解决办法:

def bytes2int( tb, order='big'):
if order == 'big': seq=[0,1,2,3]
elif order == 'little': seq=[3,2,1,0]
i = 0
for j in seq: i = (i<<8)+tb[j]
return i

十六进制(bytes2int ([0x87,0x65,0x43,0x21]))返回’0x87654321’。

它可以处理大的和小的 endianness,并且可以轻松地修改为8字节

>>> reduce(lambda s, x: s*256 + x, bytearray("y\xcc\xa6\xbb"))
2043455163

测试1: 相反:

>>> hex(2043455163)
'0x79cca6bb'

测试2: 字节数 > 8:

>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAAA"))
338822822454978555838225329091068225L

测试3: 增加1:

>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAAB"))
338822822454978555838225329091068226L

测试4: 附加一个字节,说“ A”:

>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAABA"))
86738642548474510294585684247313465921L

测试5: 除以256:

>>> reduce(lambda s, x: s*256 + x, bytearray("AAAAAAAAAAAAAABA"))/256
338822822454978555838225329091068226L

正如预期的那样,结果等于测试4的结果。

如上所述,使用 结构unpack函数是一个很好的方法。如果你想实现你自己的功能,还有另一个解决方案:

def bytes_to_int(bytes):
result = 0
for b in bytes:
result = result * 256 + int(b)
return result

使用 array.array 的一个相当快的方法,我已经使用了一段时间:

预定义变量:

offset = 0
size = 4
big = True # endian
arr = array('B')
arr.fromstring("\x00\x00\xff\x00") # 5 bytes (encoding issues) [0, 0, 195, 191, 0]

到 int: (读)

val = 0
for v in arr[offset:offset+size][::pow(-1,not big)]: val = (val<<8)|v

(写)

val = 16384
arr[offset:offset+size] = \
array('B',((val>>(i<<3))&255 for i in range(size)))[::pow(-1,not big)]

不过这些可能会更快。

编辑:
对于一些数字,下面是一个性能测试(Anaconda2.3.0) ,显示了与 reduce()相比较的稳定平均读数:

========================= byte array to int.py =========================
5000 iterations; threshold of min + 5000ns:
______________________________________code___|_______min______|_______max______|_______avg______|_efficiency
⣿⠀⠀⠀⠀⡇⢀⡀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⡀⠀⢰⠀⠀⠀⢰⠀⠀⠀⢸⠀⠀⢀⡇⠀⢀⠀⠀⠀⠀⢠⠀⠀⠀⠀⢰⠀⠀⠀⢸⡀⠀⠀⠀⢸⠀⡇⠀⠀⢠⠀⢰⠀⢸⠀
⣿⣦⣴⣰⣦⣿⣾⣧⣤⣷⣦⣤⣶⣾⣿⣦⣼⣶⣷⣶⣸⣴⣤⣀⣾⣾⣄⣤⣾⡆⣾⣿⣿⣶⣾⣾⣶⣿⣤⣾⣤⣤⣴⣼⣾⣼⣴⣤⣼⣷⣆⣴⣴⣿⣾⣷⣧⣶⣼⣴⣿⣶⣿⣶
val = 0 \nfor v in arr: val = (val<<8)|v |     5373.848ns |   850009.965ns |     ~8649.64ns |  62.128%
⡇⠀⠀⢀⠀⠀⠀⡇⠀⡇⠀⠀⣠⠀⣿⠀⠀⠀⠀⡀⠀⠀⡆⠀⡆⢰⠀⠀⡆⠀⡄⠀⠀⠀⢠⢀⣼⠀⠀⡇⣠⣸⣤⡇⠀⡆⢸⠀⠀⠀⠀⢠⠀⢠⣿⠀⠀⢠⠀⠀⢸⢠⠀⡀
⣧⣶⣶⣾⣶⣷⣴⣿⣾⡇⣤⣶⣿⣸⣿⣶⣶⣶⣶⣧⣷⣼⣷⣷⣷⣿⣦⣴⣧⣄⣷⣠⣷⣶⣾⣸⣿⣶⣶⣷⣿⣿⣿⣷⣧⣷⣼⣦⣶⣾⣿⣾⣼⣿⣿⣶⣶⣼⣦⣼⣾⣿⣶⣷
val = reduce( shift, arr ) |     6489.921ns |  5094212.014ns |   ~12040.269ns |  53.902%

这是一个原始的性能测试,所以省略了 Endian pow-lip。
所示的 shift函数应用与 for 循环相同的 shift-oring 操作,而 arr仅仅是 array.array('B',[0,0,255,0]),因为它具有仅次于 dict的最快的迭代性能。

我也许还应该注意到,效率是以平均时间的准确性来衡量的。

在 python3中,您可以通过以下方法轻松地将字节字符串转换为整数列表(0.255)

>>> list(b'y\xcc\xa6\xbb')
[121, 204, 166, 187]