如何将字节字符串转换为int?


162

如何在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

不用说,如果您每次导入都执行此方法很多次,则成比例地减少了一个问题。也可能是I / O成本而不是CPU,因此它可能取决于特定计算机的容量和负载特性。


从标准库中导入某些内容是不好的,为什么呢?

安迪威(Andyway),重复项:stackoverflow.com/questions/5415/…– 2009

26
您的“进一步更新”很奇怪...您为什么会如此频繁地导入模块?

5
我知道这是个老问题。但是,如果您想与其他人保持最新的比较:机械蜗牛的答案(int.from_bytesstruct.unpack在我的计算机上表现不佳。除了更具可读性的imo。
magu_

Answers:


110

您还可以使用struct模块来执行此操作:

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

3
警告:在64位Python版本中,“ L”实际上是8字节(不是4),因此可能会失败。
2009年

12
拉法夫:并非如此,因为格雷格(Greg)使用的是<,因此根据文档L为标准尺寸(4)“当格式字符串以'<','>','!或“ =”。” docs.python.org/library/struct.html#format-characters
安德烈·拉斯洛

59
此答案不适用于任意长度的二进制字符串。
amcnabb

4
类型具有特定的大小,它将永远不适用于任意长度的二进制字符串。如果您知道每个项目的类型,则可以设置一个for循环来处理该问题。
约书亚·奥尔森

2
“ L”实际上是uint32(4个字节)。如果像我这样需要8个字节,请使用“ Q”-> uint64。另请注意,“ l”-> int32和q-> int64
ntg

319

在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

根据您的字节字符串的字节序

这也适用于任意长度的字节字符串整数,并且通过指定,可用于以二进制补码的整数signed=True。请参阅有关的文档from_bytes


@eri慢多少?我曾经使用过struct,但是在转到py3时转换为int.from_bytes。我在接收串行数据时每毫秒调用一次此方法,因此欢迎任何加速。我一直在看
Naib

@Naib,对于os.urandom(4)我的CPU上的** 1.4 µs **(结构)与** 2.3 µs **(int.from_bytes)而言。python 3.5.2
eri

5
@eri我复活了我用来评估几种CRC方法的timeit脚本。四次运行1)结构2)int.from_bytes 3)作为#1但cython已编译,4)作为#2但cython已编译。330 ns的结构,1.14us的int(cython两者都提供了20 ns的加速...)看起来像我在转回:)这不是过早的优化,我遇到了一些讨厌的瓶颈,尤其是要发布一百万个样本-处理,并已将零件拆掉。
奈布

66

正如Greg所说的,如果要处理二进制值,则可以使用struct,但是如果您只有一个“十六进制数”,但是以字节格式,则可能需要将其转换为:

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

...与以下内容相同:

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

...除了适用于任何数量的字节。


3
“二进制值”和“但以字节格式的”十六进制数”之间的区别到底是什么?

请参阅“帮助结构”。例如。“ 001122334455” .decode('hex')无法使用struct转换为数字。
James Antill

3
顺便说一句,这个答案假设整数是按big-endian字节顺序编码的。对于小端顺序,请执行以下操作:int(''.join(reversed(s)).encode('hex'), 16)
amcnabb

1
很好,但是会很慢!猜猜这是否真的用Python编码并不重要。
MattCochrane 2015年

8

我使用以下函数在int,hex和字节之间转换数据。

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
import array
integerValue = array.array("I", 'y\xcc\xa6\xbb')[0]

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


5

在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获取格式说明符列表。


3
>>> 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的结果。


1

我一直在努力寻找适用于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)
  • 数据必须是大端的。如果您有一个小端值,则应首先将其取反:

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

当然,仅在需要任意长度时才应使用此选项。否则,请遵循更多标准方法(例如struct)。


1

如果版本> = 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个字节


1

如上文使用所提unpack的功能结构是一个很好的方式。如果要实现自己的功能,则还有另一种解决方案:

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

这不适用于已转换为字节的负数。
玛丽亚

1

在python 3中,您可以通过以下方式轻松地将字节字符串转换为整数列表(0..255)

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

0

一种使用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]

诠释为:(阅读)

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%

这是原始性能测试,因此省略了endian pow-flip。
shift显示的函数与for循环应用相同的移位或运算,并且该函数的迭代性能arr仅次于array.array('B',[0,0,255,0])dict

我可能还应该注意到,效率是通过对平均时间的准确性来衡量的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.