简体   繁体   English

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

[英]How to convert a string of bytes into an int?

How can I convert a string of bytes into an int in python?如何在python中将字节字符串转换为int?

Say like this: 'y\\xcc\\xa6\\xbb'像这样说: 'y\\xcc\\xa6\\xbb'

I came up with a clever/stupid way of doing it:我想出了一个聪明/愚蠢的方法:

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

I know there has to be something builtin or in the standard library that does this more simply...我知道必须有一些内置的东西或在标准库中可以更简单地做到这一点......

This is different from converting a string of hex digits for which you can use int(xxx, 16), but instead I want to convert a string of actual byte values.这与转换可以使用 int(xxx, 16) 的十六进制数字字符串不同,但我想转换实际字节值的字符串。

UPDATE:更新:

I kind of like James' answer a little better because it doesn't require importing another module, but Greg's method is faster:我有点喜欢 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

My hacky method:我的黑客方法:

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

FURTHER UPDATE:进一步更新:

Someone asked in comments what's the problem with importing another module.有人在评论中问导入另一个模块有什么问题。 Well, importing a module isn't necessarily cheap, take a look:好吧,导入模块不一定便宜,看看:

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

Including the cost of importing the module negates almost all of the advantage that this method has.包括导入模块的成本几乎否定了这种方法所具有的所有优势。 I believe that this will only include the expense of importing it once for the entire benchmark run;我相信这将只包括在整个基准运行中导入一次的费用; look what happens when I force it to reload every time:看看当我每次强制重新加载时会发生什么:

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

Needless to say, if you're doing a lot of executions of this method per one import than this becomes proportionally less of an issue.毋庸置疑,如果您在每次导入时执行此方法的很多次,那么这将成比例地减少问题。 It's also probably i/o cost rather than cpu so it may depend on the capacity and load characteristics of the particular machine.它也可能是 i/o 成本而不是 cpu,因此它可能取决于特定机器的容量和负载特性。

In Python 3.2 and later, use在 Python 3.2 及更高版本中,使用

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

or

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

according to the endianness of your byte-string.根据字节串​​的字节序

This also works for bytestring-integers of arbitrary length, and for two's-complement signed integers by specifying signed=True .这也适用于任意长度的字节串整数,以及通过指定signed=True二进制补码有符号整数。 See thedocs for from_bytes .请参阅from_bytes文档

You can also use the struct module to do this:您还可以使用struct模块来执行此操作:

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

As Greg said, you can use struct if you are dealing with binary values, but if you just have a "hex number" but in byte format you might want to just convert it like:正如格雷格所说,如果您正在处理二进制值,您可以使用 struct ,但如果您只有一个“十六进制数”但采用字节格式,您可能只想将其转换为:

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

...this is the same as: ...这与:

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

...except it'll work for any number of bytes. ...除了它适用于任意数量的字节。

I use the following function to convert data between int, hex and 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')

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

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

Warning: the above is strongly platform-specific.警告:以上内容是强平台特定的。 Both the "I" specifier and the endianness of the string->int conversion are dependent on your particular Python implementation. “I”说明符和 string->int 转换的字节序都取决于您的特定 Python 实现。 But if you want to convert many integers/strings at once, then the array module does it quickly.但是如果你想一次转换许多整数/字符串,那么数组模块会很快完成。

In Python 2.x, you could use the format specifiers <B for unsigned bytes, and <b for signed bytes with struct.unpack / struct.pack .在 Python 2.x 中,您可以使用格式说明符<B表示无符号字节,使用<b表示有符号字节和struct.unpack / struct.pack

Eg:例如:

Let x = '\\xff\\x10\\x11'x = '\\xff\\x10\\x11'

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

And:并且:

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

That * is required!那个*是必需的!

See https://docs.python.org/2/library/struct.html#format-characters for a list of the format specifiers. https://docs.python.org/2/library/struct.html#format-characters获取格式说明符列表。

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

Test 1: inverse:测试 1:逆:

>>> hex(2043455163)
'0x79cca6bb'

Test 2: Number of bytes > 8:测试 2:字节数 > 8:

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

Test 3: Increment by one:测试 3:加一:

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

Test 4: Append one byte, say 'A':测试 4:附加一个字节,比如“A”:

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

Test 5: Divide by 256:测试 5:除以 256:

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

Result equals the result of Test 4, as expected.正如预期的那样,结果等于测试 4 的结果。

I was struggling to find a solution for arbitrary length byte sequences that would work under Python 2.x.我正在努力寻找可以在 Python 2.x 下工作的任意长度字节序列的解决方案。 Finally I wrote this one, it's a bit hacky because it performs a string conversion, but it works.最后我写了这个,它有点hacky,因为它执行字符串转换,但它有效。

Function for Python 2.x, arbitrary length 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)

This function has two requirements:这个函数有两个要求:

  • The input data needs to be a bytearray .输入data需要是bytearray You may call the function like this:你可以这样调用函数:

     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:如果你有一个 little-endian 值,你应该先反转它:

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

Of course, this should be used only if arbitrary length is needed.当然,这应该仅在需要任意长度时使用。 Otherwise, stick with more standard ways (eg struct ).否则,坚持使用更标准的方式(例如struct )。

int.from_bytes is the best solution if you are at version >=3.2.如果你的版本 >=3.2,int.from_bytes 是最好的解决方案。 The "struct.unpack" solution requires a string so it will not apply to arrays of bytes. “struct.unpack”解决方案需要一个字符串,因此它不适用于字节数组。 Here is another solution:这是另一个解决方案:

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])) returns '0x87654321'. hex( bytes2int( [0x87, 0x65, 0x43, 0x21])) 返回'0x87654321'。

It handles big and little endianness and is easily modifiable for 8 bytes它处理大字节序和小字节序,并且很容易修改为 8 个字节

As mentioned above using unpack function of struct is a good way.如上所述,使用struct 的unpack函数是一个好方法。 If you want to implement your own function there is an another solution:如果您想实现自己的功能,还有另一种解决方案:

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

A decently speedy method utilizing array.array I've been using for some time:使用 array.array 的一种相当快速的方法,我已经使用了一段时间:

predefined variables:预定义变量:

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]

to int: (read)到 int: (阅读)

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

from int: (write)来自int:(写)

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

It's possible these could be faster though.不过,这些可能会更快。

EDIT:编辑:
For some numbers, here's a performance test (Anaconda 2.3.0) showing stable averages on read in comparison to reduce() :对于某些数字,这里有一个性能测试(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%

This is a raw performance test, so the endian pow-flip is left out.这是一个原始的性能测试,因此省略了 endian pow-flip。
The shift function shown applies the same shift-oring operation as the for loop, and arr is just array.array('B',[0,0,255,0]) as it has the fastest iterative performance next to dict .所示的shift函数应用与 for 循环相同的移位或运算操作,而arr只是array.array('B',[0,0,255,0])因为它的迭代性能array.array('B',[0,0,255,0]) dict最快。

I should probably also note efficiency is measured by accuracy to the average time.我可能还应该注意到效率是通过平均时间的准确性来衡量的。

In python 3 you can easily convert a byte string into a list of integers (0..255) by在python 3中,您可以通过以下方式轻松地将字节字符串转换为整数列表(0..255)

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM