Cómo convertir una cadena de bytes en un int en Python

¿Cómo puedo convertir una cadena de bytes en un int en python?

Diga así: 'y\xcc\xa6\xbb'

Se me ocurrió una forma inteligente / estúpida de hacerlo:

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

Sé que tiene que haber algo incorporado o en la biblioteca estándar que hace esto de manera más simple …

Esto es diferente de convertir una cadena de dígitos hexadecimales para los que puede usar int (xxx, 16), pero en su lugar, quiero convertir una cadena de valores de bytes reales.

ACTUALIZAR:

Me gusta un poco mejor la respuesta de James porque no requiere importar otro módulo, pero el método de Greg es más rápido:

 >>> from timeit import Timer >>> Timer('struct.unpack(">> Timer("int('y\xcc\xa6\xbb'.encode('hex'), 16)").timeit() 1.1432669162750244 

Mi método hacky:

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

MÁS ACTUALIZACIÓN:

Alguien preguntó en comentarios cuál es el problema con importar otro módulo. Bueno, importar un módulo no es necesariamente barato, eche un vistazo:

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

Incluir el costo de importar el módulo niega casi toda la ventaja que tiene este método. Creo que esto solo incluirá el gasto de importarlo una vez para toda la ejecución del índice de referencia; Mira lo que pasa cuando lo obligo a recargar cada vez:

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

No hace falta decir que, si está realizando muchas ejecuciones de este método por una importación, esto se convierte en un problema proporcionalmente menor. También es probable que tenga un costo de E / S en lugar de CPU, por lo que puede depender de la capacidad y las características de carga de la máquina en particular.

También puedes usar el módulo de estructura para hacer esto:

 >>> struct.unpack(" 

En Python 3.2 y versiones posteriores, use

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

o

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

De acuerdo a la endianidad de tu byte-string.

Esto también funciona para bytestring-enteros de longitud arbitraria, y para enteros con signo de complemento de dos especificando significado signed=True . Ver los documentos para from_bytes .

Como dijo Greg, puedes usar struct si estás tratando con valores binarios, pero si solo tienes un “número hexadecimal” pero en formato de byte tal vez quieras convertirlo como:

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

… esto es lo mismo que

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

… excepto que funcionará para cualquier número de bytes.

Uso la siguiente función para convertir datos entre int, hex y 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') 

Fuente: 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] 

Advertencia: lo anterior es fuertemente específico de la plataforma. Tanto el especificador “I” como el carácter endian de la conversión string-> int dependen de su implementación particular de Python. Pero si desea convertir muchos enteros / cadenas a la vez, entonces el módulo de matriz lo hace rápidamente.

En Python 2.x, puede usar los especificadores de formato para bytes sin signo, y para bytes firmados con struct.unpack / struct.pack .

P.ej:

Sea x = '\xff\x10\x11'

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

Y:

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

¡Eso es requerido!

Consulte https://docs.python.org/2/library/struct.html#format-characters para obtener una lista de los especificadores de formato.

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

Prueba 1: inverso:

 >>> hex(2043455163) '0x79cca6bb' 

Prueba 2: Número de bytes> 8:

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

Prueba 3: Incremento por uno:

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

Prueba 4: agregue un byte, diga ‘A’:

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

Prueba 5: Divide por 256:

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

El resultado es igual al resultado de la Prueba 4, como se esperaba.

int.from_bytes es la mejor solución si tiene una versión> = 3.2. La solución “struct.unpack” requiere una cadena para que no se aplique a matrices de bytes. Aquí hay otra solución:

 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])) devuelve '0x87654321'.

Maneja endianness grande y pequeño y es fácilmente modificable para 8 bytes.

Como se mencionó anteriormente, usar la función Unpack de struct es una buena manera. Si quieres implementar tu propia función hay otra solución:

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

Estaba luchando por encontrar una solución para secuencias de bytes de longitud arbitraria que funcionaran en Python 2.x. Finalmente escribí este, es un poco pirateado porque realiza una conversión de cadena, pero funciona.

Función para Python 2.x, longitud arbitraria

 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) 

Esta función tiene dos requisitos:

  • Los data entrada deben ser un bytearray . Puedes llamar a la función así:

     s = 'y\xcc\xa6\xbb' n = signedbytes(s) 
  • Los datos deben ser big-endian. En caso de que tenga un valor little-endian, primero debe revertirlo:

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

Por supuesto, esto debe usarse solo si se necesita una longitud arbitraria. De lo contrario, quédate con formas más estándar (por ejemplo, struct )