2009-01-14 18 views
107

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

decir como esto: 'y\xcc\xa6\xbb'

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

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

Sé que tiene que haber algo por el computador o en la biblioteca estándar que hace esto más simplemente ...

Esto es diferente de converting a string of hex digits para el que puede usar int (xxx, 16), pero en su lugar quiero convertir una cadena de valores de bytes reales.

ACTUALIZACIÓN:

me gusta la respuesta de James un poco mejor, ya que no requiere la importación de 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 

ADEMÁS dE ACTUALIZACIÓN:

Alguien preguntó en los comentarios cuál es la pr Oblem con la importación de otro módulo. Así, la importación de un módulo no es necesariamente barato, echar 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 toda la ventaja de que este método tiene. Creo que esto solo incluirá el gasto de importarlo una vez para la ejecución de referencia completa; mira lo que pasa cuando me fuerzo para recargar cada vez que:

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

Huelga decir que, si está haciendo una gran cantidad de ejecuciones de este método por cada importación que esto se convierte proporcionalmente menos de un problema. También es probable que tenga un costo de I/O 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.

+0

e importar algo de la lib estándar es malo, ¿por qué? – hop

+0

andyway, duplicado: http://stackoverflow.com/questions/5415/convert-bytes-to-floating-point-numbers-in-python – hop

+19

su "actualización adicional" es extraña ... ¿por qué importaría el módulo tan ¿a menudo? – hop

Respuesta

81

También puede utilizar el módulo struct para hacer esto:

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

Advertencia: "L" en realidad tiene 8 bytes (no 4) en las compilaciones de Python de 64 bits, por lo que podría fallar allí. –

+11

Rafał: No realmente, ya que Greg estaba usando <, de acuerdo con los documentos L es el tamaño estándar (4) "cuando la cadena de formato comienza con uno de '<', '>', '!' o '='. " http://docs.python.org/library/struct.html#format-characters –

+48

Esta respuesta no funciona para cadenas binarias de longitud arbitraria. – amcnabb

59

Como Greg dijo, puede utilizar struct si se trata de valores binarios, pero si sólo tienen un "número hexadecimal", pero en formato de bytes es posible que desee simplemente 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 cantidad de bytes.

+3

cuál es exactamente la diferencia entre "valores binarios" y un "número hexadecimal", pero en formato de bytes "??????? – hop

+0

Ver "estructura de ayuda". P.ej. "001122334455" .decode ('hex') no puede convertirse a un número usando struct. –

+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 (invertido (s)). Encode ('hex'), 16)' – amcnabb

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 la endianidad de la conversión string-> int dependen de su implementación específica de Python. Pero si quiere convertir muchos enteros/cadenas a la vez, entonces el módulo de matriz lo hace rápidamente.

206

En Python 3.2 y más tarde, utilice

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

o

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

según la endianness de su byte de cuerdas.

Esto también funciona para enteros de bytes de longitud arbitraria, y para enteros con signo de complemento de dos especificando signed=True. Vea el docs for from_bytes.

+0

@eri ¿cuánto más lento? Solía ​​usar struct pero convertí a int.from_bytes cuando fui a py3.Estoy llamando a este método cada vez que recibo datos en serie, por lo que cualquier aceleración es bienvenida. He estado buscando en este – Naib

+0

@Naib, para 'os.urandom (4)' bytes ** 1.4 μs ** (struct) contra ** 2.3 μs ** (int.from_bytes) en mi CPU. Python 3.5.2 – eri

+2

@eri Resucité un script de tiempo que solía evaluar un par de métodos de CRC. Cuatro ejecuciones 1) struct 2) int.from_bytes 3) como # 1 pero compilada por cython, 4) como # 2 pero compilada por cython. 330ns para struct, 1.14us para int (Cython dio quizás 20ns de aceleración en ambos ...) parece que estoy regresando :) 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 han estado quitando partes. – Naib

7

Utilizo 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

4

En Python 2.x, podría utilizar los especificadores de formato <B de bytes sin signo, y <b de bytes firmados con struct.unpack/struct.pack.

por ejemplo:

Deje 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 requerida!

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

0

Estaba luchando por encontrar una solución para secuencias de bytes de longitud arbitraria que funcionaría en 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 data necesita ser un bytearray. Usted puede llamar a la función como esta:

    s = 'y\xcc\xa6\xbb' 
    n = signedbytes(s) 
    
  • los datos tienen que ser grandes-endian. En caso de que tenga un valor ascendente hacia la izquierda, usted debe invertir en primer lugar:

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

Por supuesto, esto sólo debe utilizarse si se necesita longitud arbitraria. De lo contrario, cumpla con otras formas estándar (por ejemplo, struct).

1

int.from_bytes es la mejor solución si se encuentra 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í es 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

1
>>> 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 por uno:

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

Prueba 4: Añadir un byte, por ejemplo 'A':

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

Prueba 5: Dividir 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.

0

Como se mencionó anteriormente utilizando la función unpack 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 
Cuestiones relacionadas