如何将一串字节转换为int?
Posted
技术标签:
【中文标题】如何将一串字节转换为int?【英文标题】:How to convert a string of bytes into an int? 【发布时间】:2010-10-01 11:17:02 【问题描述】:如何在 python 中将一串字节转换为 int?
这样说:'y\xcc\xa6\xbb'
我想出了一个聪明/愚蠢的方法:
sum(ord(c) << (i * 8) for i, c in enumerate('y\xcc\xa6\xbb'[::-1]))
我知道必须有一些内置的或标准库中的东西可以更简单地做到这一点......
这与 converting a string of hex digits 不同,您可以使用 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
我的hacky方法:
>>> Timer("sum(ord(c) << (i * 8) for i, c in enumerate('y\xcc\xa6\xbb'[::-1]))").timeit()
2.8819329738616943
进一步更新:
有人在 cmets 中询问导入另一个模块有什么问题。好吧,导入模块不一定便宜,看看:
>>> 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
不用说,如果您在每次导入时要执行大量此方法,那么这成比例地就不是问题了。它也可能是 i/o 成本而不是 cpu,因此它可能取决于特定机器的容量和负载特性。
【问题讨论】:
从标准库中导入一些东西不好,为什么? 另外,重复:***.com/questions/5415/… 您的“进一步更新”很奇怪……为什么要经常导入模块? 我知道这是个老问题。但是,如果您想让其他人的比较保持最新:在我的计算机上,机械蜗牛的回答 (int.from_bytes
) 的表现优于 struct.unpack
。接下来是更具可读性的 imo。
【参考方案1】:
您也可以使用struct 模块来执行此操作:
>>> struct.unpack("<L", "y\xcc\xa6\xbb")[0]
3148270713L
【讨论】:
警告:在 64 位 Python 版本中,“L”实际上是 8 个字节(而不是 4 个),所以这可能会失败。 Rafał:不是真的,因为 Greg 使用的是 '、'!' 之一开头时或'='。” docs.python.org/library/struct.html#format-characters 此答案不适用于任意长度的二进制字符串。 类型有特定的大小,它永远不会适用于任意长度的二进制字符串。如果您知道每个项目的类型,您可以设置一个 for 循环来处理它。 "L" 实际上是 uint32(4 个字节)。如果在我的情况下您需要 8 个字节,请使用“Q”-->uint64。还要注意 "l"-->int32 和 q-->int64【参考方案2】:正如 Greg 所说,如果您正在处理二进制值,则可以使用 struct,但如果您只有一个“十六进制数”但采用字节格式,您可能只想将其转换为:
s = 'y\xcc\xa6\xbb'
num = int(s.encode('hex'), 16)
...这与:
num = struct.unpack(">L", s)[0]
...除了它适用于任意数量的字节。
【讨论】:
“二进制值”和“'hex number' but in byte format”之间到底有什么区别??????? 参见“帮助结构”。例如。 "001122334455".decode('hex') 无法使用 struct 转换为数字。 顺便说一句,这个答案假设整数以大端字节顺序编码。对于小端顺序,请执行以下操作:int(''.join(reversed(s)).encode('hex'), 16)
很好,但这会很慢!如果你用 Python 编码,猜猜这并不重要。【参考方案3】:
import array
integerValue = array.array("I", 'y\xcc\xa6\xbb')[0]
警告:以上内容是特定于平台的。 "I" 说明符和 string->int 转换的字节顺序都取决于您特定的 Python 实现。但是如果你想一次转换多个整数/字符串,那么数组模块会很快。
【讨论】:
【参考方案4】:在 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。
这也适用于任意长度的字节串整数,以及通过指定signed=True
的二进制补码有符号整数。请参阅docs for from_bytes
。
【讨论】:
@eri 慢了多少?我曾经使用 struct,但当我去 py3 时转换为 int.from_bytes。当我接收串行数据时,我每毫秒调用一次此方法,因此欢迎任何加速。我一直在看这个 @Naib,对于我的 cpu 上的os.urandom(4)
bytes **1.4 µs**(struct) vs **2.3 µs**(int.from_bytes)。 python 3.5.2
@eri 我复活了一个用于评估几个 CRC 方法的 timeit 脚本。四次运行 1) struct 2) int.from_bytes 3) 作为 #1 但 cython 已编译,4) 作为 #2 但 cython 已编译。 struct 为 330ns,int 为 1.14us(cython 可能在两者中都提供了 20ns 的加速......)看起来我正在切换回来:) 这不是过早的优化,我遇到了一些令人讨厌的瓶颈,尤其是要发布一百万个样本-处理并一直在敲掉零件。【参考方案5】:
我使用以下函数在 int、hex 和 bytes 之间转换数据。
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
【讨论】:
【参考方案6】:在 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'
*
是必需的!
有关格式说明符的列表,请参阅https://docs.python.org/2/library/struct.html#format-characters。
【讨论】:
【参考方案7】:我一直在努力寻找可以在 Python 2.x 下工作的任意长度字节序列的解决方案。最后我写了这个,有点hacky,因为它执行字符串转换,但它可以工作。
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)
数据需要是大端的。如果你有一个 little-endian 值,你应该先反转它:
n = signedbytes(s[::-1])
当然,只有在需要任意长度时才应该使用它。否则,请坚持使用更标准的方式(例如struct
)。
【讨论】:
【参考方案8】:如果您的版本 >=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
hex(bytes2int([0x87, 0x65, 0x43, 0x21])) 返回 '0x87654321'。
它处理大小字节序,并且很容易修改为 8 个字节
【讨论】:
【参考方案9】:>>> 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:加一:
>>> 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 的结果,正如预期的那样。
【讨论】:
【参考方案10】:如上所述,使用 struct 的unpack
函数是一个好方法。如果您想实现自己的功能,还有另一种解决方案:
def bytes_to_int(bytes):
result = 0
for b in bytes:
result = result * 256 + int(b)
return result
【讨论】:
这不适用于转换为字节的负数。【参考方案11】:我已经使用了一段时间的利用 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
从 int:(写)
val = 16384
arr[offset:offset+size] = \
array('B',((val>>(i<<3))&255 for i in range(size)))[::pow(-1,not big)]
不过,这些可能会更快。
编辑:
对于某些数字,这是一个性能测试 (Anaconda 2.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%
这是一个原始性能测试,因此忽略了字节序翻转。
所示的 shift
函数应用与 for 循环相同的移位或运算,而 arr
只是 array.array('B',[0,0,255,0])
,因为它具有仅次于 dict
的最快迭代性能。
我可能还应该注意效率是通过平均时间的准确性来衡量的。
【讨论】:
【参考方案12】:在 python 3 中,您可以通过
轻松地将字节字符串转换为整数列表 (0..255)>>> list(b'y\xcc\xa6\xbb')
[121, 204, 166, 187]
【讨论】:
以上是关于如何将一串字节转换为int?的主要内容,如果未能解决你的问题,请参考以下文章