Wie konvertiere ich eine Folge von Bytes in ein int?

162

Wie kann ich eine Byte-Zeichenfolge in Python in ein Int konvertieren?

Sagen Sie so: 'y\xcc\xa6\xbb'

Ich habe mir eine clevere / dumme Methode ausgedacht:

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

Ich weiß, dass es etwas Eingebautes oder in der Standardbibliothek geben muss, das dies einfacher macht ...

Dies unterscheidet sich von der Konvertierung einer Zeichenfolge mit Hex-Ziffern, für die Sie int (xxx, 16) verwenden können. Stattdessen möchte ich eine Zeichenfolge mit tatsächlichen Bytewerten konvertieren.

AKTUALISIEREN:

Ich mag James 'Antwort ein bisschen besser, weil es nicht erforderlich ist, ein anderes Modul zu importieren, aber Gregs Methode ist schneller:

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

Meine hackige Methode:

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

WEITERES UPDATE:

Jemand fragte in Kommentaren, was das Problem beim Importieren eines anderen Moduls sei. Der Import eines Moduls ist nicht unbedingt billig.

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

Durch die Einbeziehung der Kosten für den Import des Moduls wird fast der gesamte Vorteil dieser Methode zunichte gemacht. Ich glaube, dass dies nur die Kosten für den einmaligen Import für den gesamten Benchmark-Lauf beinhaltet. Schauen Sie, was passiert, wenn ich es jedes Mal zum Neuladen zwinge:

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

Es ist unnötig zu erwähnen, dass, wenn Sie viele Ausführungen dieser Methode pro Import durchführen, dies proportional weniger problematisch wird. Es ist wahrscheinlich auch eher E / A-Kosten als CPU, so dass es von der Kapazität und den Lasteigenschaften der jeweiligen Maschine abhängen kann.

ʞɔıu
quelle
und etwas aus der Standardbibliothek zu importieren ist schlecht, warum?
26
Ihr "weiteres Update" ist komisch ... warum sollten Sie das Modul so oft importieren?
5
Ich weiß, das ist eine alte Frage. Aber wenn Sie Ihren Vergleich für andere Personen auf dem neuesten Stand halten möchten: Die Antwort ( int.from_bytes) der mechanischen Schnecke wurde struct.unpackauf meinem Computer übertroffen . Neben besser lesbar imo.
Magu_

Antworten:

110

Sie können dazu auch das struct- Modul verwenden:

>>> struct.unpack("<L", "y\xcc\xa6\xbb")[0]
3148270713L
Greg Hewgill
quelle
3
Warnung: "L" ist tatsächlich 8 Byte (nicht 4) in 64-Bit-Python-Builds, daher kann dies dort fehlschlagen.
Rafał Dowgird
12
Rafał: Nicht wirklich, da Greg <verwendet hat, ist L laut Dokument Standardgröße (4) ", wenn die Formatzeichenfolge mit einem von '<', '>', '!" Beginnt. oder '='. " docs.python.org/library/struct.html#format-characters
André Laszlo
59
Diese Antwort funktioniert nicht für Binärzeichenfolgen beliebiger Länge.
Amcnabb
4
Typen haben bestimmte Größen. Für Binärzeichenfolgen beliebiger Länge funktioniert dies niemals. Sie können eine for-Schleife einrichten, um dies zu handhaben, wenn Sie den Typ jedes Elements kennen.
Joshua Olson
2
"L" ist eigentlich uint32 (4 Bytes). Wenn Sie wie in meinem Fall 8 Bytes benötigen, verwenden Sie "Q" -> uint64. Beachten Sie auch, dass "l" -> int32 und q -> int64
ntg
319

Verwenden Sie in Python 3.2 und höher

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

oder

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

entsprechend der Endianness Ihres Byte-Strings.

Dies funktioniert auch für Bytestring-Ganzzahlen beliebiger Länge und für Zwei-Komplement-Ganzzahlen mit Vorzeichen durch Angabe signed=True. Siehe die Dokumente fürfrom_bytes .

Mechanische Schnecke
quelle
@eri wie viel langsamer? Früher habe ich struct verwendet, aber bei py3 in int.from_bytes konvertiert. Ich rufe diese Methode alle ms auf, während ich serielle Daten empfange, daher ist jede Beschleunigung willkommen. Ich habe mir das
angeschaut
@Naib, für os.urandom(4)Bytes ** 1,4 µs ** (struct) vs ** 2,3 µs ** (int.from_bytes) auf meiner CPU. Python 3.5.2
Eri
5
@eri Ich habe ein Timeit-Skript wiederbelebt, mit dem ich einige CRC-Methoden evaluiert habe. Vier Läufe 1) Struktur 2) int.from_bytes 3) als # 1, aber Cython kompiliert, 4) als # 2, aber Cython kompiliert. 330ns für struct, 1.14us für int (cython hat in beiden Fällen vielleicht 20ns beschleunigt ...) sieht so aus, als würde ich zurückwechseln :) Dies ist keine vorzeitige Optimierung, ich habe einige böse Engpässe festgestellt, insbesondere mit einer Million zu postender Samples -Prozess und haben Teile abgeschlagen.
Naib
66

Wie Greg sagte, können Sie struct verwenden, wenn Sie mit Binärwerten arbeiten, aber wenn Sie nur eine "Hex-Zahl" haben, aber im Byte-Format, möchten Sie sie vielleicht einfach wie folgt konvertieren:

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

... das ist das gleiche wie:

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

... außer es funktioniert für eine beliebige Anzahl von Bytes.

James Antill
quelle
3
Was genau ist der Unterschied zwischen "Binärwerten" und einer "Hexadezimalzahl", aber im Byteformat "???????
Siehe "Hilfestruktur". Z.B. "001122334455" .decode ('hex') kann mit struct nicht in eine Zahl konvertiert werden.
James Antill
3
Bei dieser Antwort wird übrigens davon ausgegangen, dass die Ganzzahl in der Big-Endian-Bytereihenfolge codiert ist. Für Little-Endian-Bestellung, tun Sie:int(''.join(reversed(s)).encode('hex'), 16)
Amcnabb
1
gut, aber das wird langsam! Vermutlich spielt das keine Rolle, wenn Sie in Python programmieren.
MattCochrane
8

Ich benutze die folgende Funktion, um Daten zwischen int, hex und Bytes zu konvertieren.

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

Quelle: http://opentechnotes.blogspot.com.au/2014/04/convert-values-to-from-integer-hex.html

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

Warnung: Das oben Genannte ist stark plattformspezifisch. Sowohl der "I" -Spezifizierer als auch die Endianness der string-> int-Konvertierung hängen von Ihrer speziellen Python-Implementierung ab. Wenn Sie jedoch viele Ganzzahlen / Zeichenfolgen gleichzeitig konvertieren möchten, erledigt das Array-Modul dies schnell.

Rafał Dowgird
quelle
5

In Python 2.x können Sie die Formatbezeichner <Bfür vorzeichenlose Bytes und <bfür vorzeichenbehaftete Bytes mit struct.unpack/ verwenden struct.pack.

Z.B:

Sei x='\xff\x10\x11'

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

Und:

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

Das *ist erforderlich!

Sehen https://docs.python.org/2/library/struct.html#format-characters für eine Liste der Formatspezifizierer.

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

Test 1: invers:

>>> hex(2043455163)
'0x79cca6bb'

Test 2: Anzahl der Bytes> 8:

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

Test 3: Inkrement um eins:

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

Test 4: Fügen Sie ein Byte hinzu und sagen Sie 'A':

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

Test 5: Teilen durch 256:

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

Das Ergebnis entspricht erwartungsgemäß dem Ergebnis von Test 4.

user3076105
quelle
1

Ich hatte Mühe, eine Lösung für Byte-Sequenzen beliebiger Länge zu finden, die unter Python 2.x funktionieren würden. Schließlich habe ich dieses geschrieben, es ist ein bisschen hacky, weil es eine String-Konvertierung durchführt, aber es funktioniert.

Funktion für Python 2.x, beliebige Länge

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)

Diese Funktion hat zwei Anforderungen:

  • Die Eingabe datamuss a sein bytearray. Sie können die Funktion folgendermaßen aufrufen:

    s = 'y\xcc\xa6\xbb'
    n = signedbytes(s)
  • Die Daten müssen Big-Endian sein. Wenn Sie einen Little-Endian-Wert haben, sollten Sie ihn zuerst umkehren:

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

Dies sollte natürlich nur verwendet werden, wenn eine beliebige Länge benötigt wird. Ansonsten bleiben Sie bei den Standardmethoden (z struct. B. ).

Andrea Lazzarotto
quelle
1

int.from_bytes ist die beste Lösung, wenn Sie Version> = 3.2 verwenden. Die "struct.unpack" -Lösung erfordert eine Zeichenfolge, sodass sie nicht für Arrays von Bytes gilt. Hier ist eine andere Lösung:

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])) gibt '0x87654321' zurück.

Es verarbeitet große und kleine Endianness und kann leicht für 8 Bytes geändert werden

user3435121
quelle
1

Wie oben erwähnt, ist die Verwendung der unpackFunktion struct ein guter Weg. Wenn Sie Ihre eigene Funktion implementieren möchten, gibt es eine andere Lösung:

def bytes_to_int(bytes):
    result = 0
    for b in bytes:
        result = result * 256 + int(b)
return result
abdullahselek
quelle
Dies funktioniert nicht für negative Zahlen, die in Bytes konvertiert wurden.
Maria
1

In Python 3 können Sie eine Byte-Zeichenfolge einfach in eine Liste von Ganzzahlen (0..255) konvertieren

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

Eine anständig schnelle Methode mit array.array, die ich seit einiger Zeit verwende:

vordefinierte Variablen:

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]

zu int: (lesen)

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

von int: (schreiben)

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

Es ist jedoch möglich, dass diese schneller sind.

BEARBEITEN:
Für einige Zahlen ist hier ein Leistungstest (Anaconda 2.3.0), der stabile Durchschnittswerte beim Lesen im Vergleich zu reduce(): zeigt.

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

Dies ist ein roher Leistungstest, daher wird der Endian-Pow-Flip weggelassen.
Die shiftgezeigte Funktion wendet dieselbe Verschiebungsoperation wie die for-Schleife an und arrist genau so, array.array('B',[0,0,255,0])wie sie nebenan die schnellste iterative Leistung aufweist dict.

Ich sollte wahrscheinlich auch beachten, dass die Effizienz an der Genauigkeit der Durchschnittszeit gemessen wird.

Tcll
quelle