Come convertire una stringa di byte in un int?


162

Come posso convertire una stringa di byte in un int in Python?

Dì così: 'y\xcc\xa6\xbb'

Ho trovato un modo intelligente / stupido di farlo:

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

So che ci deve essere qualcosa incorporato o nella libreria standard che lo fa più semplicemente ...

Ciò è diverso dalla conversione di una stringa di cifre esadecimali per cui è possibile utilizzare int (xxx, 16), ma invece voglio convertire una stringa di valori di byte effettivi.

AGGIORNARE:

Mi piace la risposta di James un po 'meglio perché non richiede l'importazione di un altro modulo, ma il metodo di Greg è più veloce:

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

Il mio metodo hacky:

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

ULTERIORI AGGIORNAMENTI:

Qualcuno ha chiesto nei commenti qual è il problema con l'importazione di un altro modulo. Bene, l'importazione di un modulo non è necessariamente economica, dai un'occhiata:

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

Includere il costo dell'importazione del modulo annulla quasi tutti i vantaggi di questo metodo. Ritengo che ciò includerà le spese di importazione una sola volta per l'intero ciclo di riferimento; guarda cosa succede quando lo costringo a ricaricare ogni volta:

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

Inutile dire che se si eseguono molte esecuzioni di questo metodo per una importazione, questo diventa proporzionalmente meno un problema. Probabilmente è anche un costo di I / O piuttosto che CPU, quindi potrebbe dipendere dalla capacità e dalle caratteristiche di carico della macchina particolare.


e importare qualcosa dalla lib standard è male, perché?


26
il tuo "ulteriore aggiornamento" è strano ... perché dovresti importare il modulo così spesso?

5
So che questa è una vecchia domanda. Ma se vuoi mantenere aggiornato il tuo confronto con altre persone: la risposta della lumaca meccanica ( int.from_bytes) eseguita struct.unpacksul mio computer. Accanto ad essere più leggibile imo.
magu_

Risposte:


110

Puoi anche usare il modulo struct per fare questo:

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

3
Avvertenza: "L" è in realtà 8 byte (non 4) nelle build Python a 64 bit, quindi potrebbe non riuscire lì.
Rafał Dowgird,

12
Rafał: Non proprio, dato che Greg stava usando <, secondo i documenti L è la dimensione standard (4) "quando la stringa di formato inizia con una di '<', '>', '!' o '='. " docs.python.org/library/struct.html#format-characters
André Laszlo

59
Questa risposta non funziona per stringhe binarie di lunghezza arbitraria.
amcnabb,

4
I tipi hanno dimensioni specifiche, non funzioneranno mai per stringhe binarie di lunghezza arbitraria. È possibile impostare un ciclo for per gestirlo se si conosce il tipo di ciascun elemento.
Joshua Olson,

2
"L" è in realtà uint32 (4 byte). Se come nel mio caso hai bisogno di 8 byte, usa "Q" -> uint64. Nota anche che "l" -> int32 e q -> int64
ntg

319

In Python 3.2 e versioni successive, utilizzare

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

o

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

in base all'endianness della stringa di byte.

Questo funziona anche per numeri interi di lunghezza arbitraria e per numeri interi con segno del complemento a due specificando signed=True. Vedi i documenti perfrom_bytes .


@eri quanto più lentamente? Ero abituato a utilizzare struct ma convertito in int.from_bytes quando sono passato a py3. Sto chiamando questo metodo ogni ms mentre ricevo dati seriali, quindi qualsiasi accelerazione è benvenuta. Ho visto questo
Naib il

@Naib, per os.urandom(4)byte ** 1,4 µs ** (struct) vs ** 2,3 µs ** (int.from_bytes) sulla mia CPU. python 3.5.2
eri il

5
@eri Ho resuscitato uno script timeit che ho usato per valutare un paio di metodi CRC. Quattro esecuzioni 1) struct 2) int.from_bytes 3) come numero 1 ma compilato da cython, 4) come numero 2 ma compilato da cython. 330ns per struct, 1.14us per int (cython ha dato forse 20ns di accelerazione in entrambi ...) sembra che stia tornando indietro :) questa non è ottimizzazione prematura, sto colpendo alcuni colli di bottiglia cattivi, specialmente con un milione di campioni da pubblicare -processo e hanno eliminato parti.
Naib,

66

Come ha detto Greg, puoi usare struct se hai a che fare con valori binari, ma se hai solo un "numero esadecimale" ma in formato byte potresti voler semplicemente convertirlo come:

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

... è lo stesso di:

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

... tranne che funzionerà per qualsiasi numero di byte.


3
qual è esattamente la differenza tra "valori binari" e un "'numero esadecimale' ma in formato byte" ???????

Vedi "help struct". Per esempio. "001122334455" .decode ('hex') non può essere convertito in un numero usando struct.
James Antill,

3
A proposito, questa risposta presuppone che l'intero sia codificato in ordine di byte big-endian. Per un ordine little-endian, int(''.join(reversed(s)).encode('hex'), 16)
esegui

1
bene ma sarà lento! Indovina che non importa se stai scrivendo codice in Python.
MattCochrane,

8

Uso la seguente funzione per convertire i dati tra int, hex e byte.

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')

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

Attenzione: quanto sopra è fortemente specifico per la piattaforma. Sia l'identificatore "I" che l'endianness della conversione string-> int dipendono dalla tua particolare implementazione Python. Ma se vuoi convertire molti numeri interi / stringhe contemporaneamente, il modulo array lo fa rapidamente.


5

In Python 2.x, è possibile utilizzare gli identificatori di formato <Bper byte non firmati e <bper byte firmati con struct.unpack/ struct.pack.

Per esempio:

Let x='\xff\x10\x11'

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

E:

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

Questo *è richiesto!

Vedere https://docs.python.org/2/library/struct.html#format-characters per un elenco degli identificatori di formato.


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

Test 1: inverso:

>>> hex(2043455163)
'0x79cca6bb'

Test 2: numero di byte> 8:

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

Test 3: incremento di uno:

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

Test 4: aggiungi un byte, di '"A":

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

Test 5: Dividi per 256:

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

Il risultato è uguale al risultato del Test 4, come previsto.


1

Stavo lottando per trovare una soluzione per sequenze di byte di lunghezza arbitraria che funzionasse con Python 2.x. Alla fine ho scritto questo, è un po 'confuso perché esegue una conversione di stringhe, ma funziona.

Funzione per Python 2.x, lunghezza 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)

Questa funzione ha due requisiti:

  • L'input datadeve essere a bytearray. È possibile chiamare la funzione in questo modo:

    s = 'y\xcc\xa6\xbb'
    n = signedbytes(s)
  • I dati devono essere big-endian. Se hai un valore little-endian, dovresti prima invertirlo:

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

Naturalmente, questo dovrebbe essere usato solo se è necessaria una lunghezza arbitraria. Altrimenti, attenersi a metodi più standard (ad es struct.).


1

int.from_bytes è la soluzione migliore se si è in versione> = 3.2. La soluzione "struct.unpack" richiede una stringa, quindi non si applica alle matrici di byte. Ecco un'altra soluzione:

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

Gestisce endianness grandi e piccoli ed è facilmente modificabile per 8 byte


1

Come accennato in precedenza, utilizzare la unpackfunzione di struct è un buon modo. Se vuoi implementare la tua funzione c'è un'altra soluzione:

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

Questo non funziona per il numero negativo che è stato convertito in byte.
Maria,

1

In python 3 puoi facilmente convertire una stringa di byte in un elenco di numeri interi (0..255) per

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

0

Un metodo abbastanza veloce che utilizza array.array che sto usando da un po 'di tempo:

variabili predefinite:

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]

a int: (leggi)

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

da int: (scrivi)

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

È possibile che questi potrebbero essere più veloci però.

EDIT:
per alcuni numeri, ecco un test delle prestazioni (Anaconda 2.3.0) che mostra le medie stabili in lettura rispetto a 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%

Questo è un test delle prestazioni non elaborato, quindi l'endian pow-flip viene lasciato fuori.
La shiftfunzione mostrata applica la stessa operazione shift-oring del ciclo for ed arrè proprio array.array('B',[0,0,255,0])come ha le prestazioni iterative più veloci accanto adict .

Probabilmente dovrei anche notare che l'efficienza è misurata dalla precisione al tempo medio.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.