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

162

¿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 haga 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 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("<L", "y\xcc\xa6\xbb")[0]', 'import struct').timeit()
0.36242198944091797
>>> 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

ACTUALIZACIÓN ADICIONAL:

Alguien preguntó en los comentarios cuál es el problema con la importación de 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

Incluyendo el costo de importar el módulo niega casi todas las ventajas que tiene este método. Creo que esto solo incluirá el gasto de importarlo una vez para toda la prueba de referencia; mira lo que sucede 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 realiza muchas ejecuciones de este método por importación, esto proporcionalmente será un problema menor. También es probable que sea un costo de E / S en lugar de una CPU, por lo que puede depender de la capacidad y las características de carga de la máquina en particular.

ʞɔıu
fuente
e importar algo de la lib estándar es malo, ¿por qué?
26
su "actualización adicional" es extraña ... ¿por qué importaría el módulo con tanta frecuencia?
55
Sé que esta es una vieja pregunta. Pero si desea mantener su comparación actualizada para otras personas: la respuesta del caracol mecánico ( int.from_bytes) se superó struct.unpacken mi computadora. Junto a ser más legible imo.
magu_

Respuestas:

110

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

>>> struct.unpack("<L", "y\xcc\xa6\xbb")[0]
3148270713L
Greg Hewgill
fuente
3
Advertencia: "L" es en realidad 8 bytes (no 4) en compilaciones de Python de 64 bits, por lo que esto podría fallar allí.
Rafał Dowgird el
12
Rafał: No realmente, ya que Greg estaba usando <, según los documentos L es tamaño estándar (4) "cuando la cadena de formato comienza con uno de '<', '>', '!' o '='. " docs.python.org/library/struct.html#format-characters
André Laszlo
59
Esta respuesta no funciona para cadenas binarias de longitud arbitraria.
amcnabb
44
Los tipos tienen tamaños específicos, nunca funcionará para cadenas binarias de longitud arbitraria. Puede configurar un bucle for para manejar eso si conoce el tipo de cada elemento.
Joshua Olson el
2
"L" es en realidad uint32 (4 bytes). Si, como en mi caso, necesita 8 bytes, use "Q" -> uint64. También tenga en cuenta que "l" -> int32 y q -> int64
ntg
319

En Python 3.2 y posterior, 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 con el endianness de su cadena de bytes.

Esto también funciona para enteros de bytes de longitud arbitraria y para enteros con signo de complemento a dos especificando signed=True. Ver los documentos parafrom_bytes .

Caracol mecánico
fuente
@eri, ¿cuánto más lento? Solía ​​usar struct pero convertí a int.from_bytes cuando fui a py3. Llamo a este método cada ms ya que recibo datos en serie, por lo que cualquier aceleración es bienvenida. He estado mirando esto
Naib
@Naib, para os.urandom(4)bytes ** 1.4 µs ** (struct) vs ** 2.3 µs ** (int.from_bytes) en mi CPU. python 3.5.2
eri
55
@eri Resucité un script timeit que usé para evaluar un par de métodos CRC. Cuatro ejecuciones 1) struct 2) int.from_bytes 3) como # 1 pero compilado por cython, 4) como # 2 pero compilado por cython. 330ns para struct, 1.14us para int (cython dio quizás 20ns de aceleración en ambos ...) parece que estoy cambiando :) esto no es una optimización prematura, he estado golpeando algunos cuellos de botella desagradables, especialmente con un millón de muestras para publicar -proceso y he estado eliminando partes.
Naib
66

Como dijo Greg, puede usar struct si está tratando con valores binarios, pero si solo tiene un "número hexadecimal" pero en formato de bytes, es posible que desee 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.

James Antill
fuente
3
¿Cuál es exactamente la diferencia entre "valores binarios" y un "'número hexadecimal' pero en formato de byte" ???????
Ver "estructura de ayuda". P.ej. "001122334455" .decode ('hex') no se puede convertir a un número usando struct.
James Antill el
3
Por cierto, esta respuesta supone que el entero está codificado en orden de bytes big-endian. Para el orden little-endian, haz:int(''.join(reversed(s)).encode('hex'), 16)
amcnabb
1
bueno pero esto va a ser lento! Supongo que eso realmente no importa si estás codificando en Python.
MattCochrane el
8

Utilizo la siguiente función para convertir datos entre int, hexadecimal 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

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

Advertencia: lo anterior es muy específico de la plataforma. Tanto el especificador "I" como el endianness 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.

Rafał Dowgird
fuente
5

En Python 2.x, puede usar los especificadores de formato <Bpara bytes sin firmar y <bpara bytes con struct.unpack/ struct.pack.

P.ej:

Let 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!

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

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

Prueba 1: inversa:

>>> hex(2043455163)
'0x79cca6bb'

Prueba 2: Número de bytes> 8:

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

Prueba 3: Incremento en 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: dividir entre 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.

usuario3076105
fuente
1

Estaba luchando por encontrar una solución para secuencias arbitrarias de bytes de longitud que funcionaran con Python 2.x. Finalmente escribí este, es un poco hacky 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:

  • La entrada datadebe ser a bytearray. Puede llamar a la función de esta manera:

    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édese con formas más estándar (por ejemplo struct).

Andrea Lazzarotto
fuente
1

int.from_bytes es la mejor solución si está en la versión> = 3.2. La solución "struct.unpack" requiere una cadena para que no se aplique a las 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

usuario3435121
fuente
1

Como se mencionó anteriormente, usar la unpackfunción de struct es una buena manera. Si desea implementar su propia función, existe otra solución:

def bytes_to_int(bytes):
    result = 0
    for b in bytes:
        result = result * 256 + int(b)
return result
abdullahselek
fuente
Esto no funciona para el número negativo que se convirtió a bytes.
Maria
1

En python 3 puede convertir fácilmente una cadena de bytes en una lista de enteros (0..255) por

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

Un método bastante rápido que utiliza array.array que he estado usando durante algún tiempo:

variables predefinidas:

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: (leer)

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

de int: (escribir)

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

Sin embargo, es posible que estos sean más rápidos.

EDITAR:
para algunos números, aquí hay una prueba de rendimiento (Anaconda 2.3.0) que muestra promedios estables en lectura en comparación conreduce() :

========================= 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%

Esta es una prueba de rendimiento sin procesar, por lo que se omite el cambio de potencia endian.
La shiftfunción mostrada aplica la misma operación de cambio de orden que el bucle for, yarr es igual array.array('B',[0,0,255,0])que tiene el rendimiento iterativo más rápido al lado dict.

Probablemente también debería notar que la eficiencia se mide por la precisión del tiempo promedio.

Tcll
fuente