Comment convertir une chaîne d'octets en un entier?

162

Comment puis-je convertir une chaîne d'octets en un entier en python?

Dis comme ça: 'y\xcc\xa6\xbb'

J'ai trouvé une façon intelligente / stupide de le faire:

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

Je sais qu'il doit y avoir quelque chose de intégré ou dans la bibliothèque standard qui fait cela plus simplement ...

Ceci est différent de la conversion d'une chaîne de chiffres hexadécimaux pour laquelle vous pouvez utiliser int (xxx, 16), mais je souhaite plutôt convertir une chaîne de valeurs d'octets réelles.

METTRE À JOUR:

J'aime un peu mieux la réponse de James car elle ne nécessite pas d'importer un autre module, mais la méthode de Greg est plus rapide:

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

Ma méthode hacky:

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

AUTRE MISE À JOUR:

Quelqu'un a demandé dans les commentaires quel était le problème lors de l'importation d'un autre module. Eh bien, importer un module n'est pas forcément bon marché, jetez un œil:

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

Le fait d'inclure le coût d'importation du module annule presque tous les avantages de cette méthode. Je pense que cela n'inclura que les frais de son importation une fois pour toute la période de référence; regardez ce qui se passe quand je le force à se recharger à chaque fois:

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

Inutile de dire que si vous effectuez beaucoup d'exécutions de cette méthode par importation, cela devient proportionnellement moins problématique. C'est aussi probablement un coût d'E / S plutôt qu'un CPU, donc cela peut dépendre de la capacité et des caractéristiques de charge de la machine particulière.

ʞɔıu
la source
et importer quelque chose de la bibliothèque standard est mauvais, pourquoi?
26
votre "mise à jour supplémentaire" est bizarre ... pourquoi importeriez-vous le module si souvent?
5
Je sais que c'est une vieille question. Mais si vous voulez garder votre comparaison à jour pour d'autres personnes: la réponse d'escargot mécanique ( int.from_bytes) a surpassé struct.unpacksur mon ordinateur. En plus d'être plus lisible imo.
magu_

Réponses:

110

Vous pouvez également utiliser le module struct pour ce faire:

>>> struct.unpack("<L", "y\xcc\xa6\xbb")[0]
3148270713L
Greg Hewgill
la source
3
Attention: "L" est en fait 8 octets (pas 4) dans les versions 64 bits de Python, donc cela pourrait échouer là-bas.
Rafał Dowgird le
12
Rafał: Pas vraiment, puisque Greg utilisait <, selon la documentation L est de taille standard (4) "lorsque la chaîne de format commence par l'un des '<', '>', '!' ou '='. " docs.python.org/library/struct.html#format-characters
André Laszlo
59
Cette réponse ne fonctionne pas pour les chaînes binaires de longueur arbitraire.
amcnabb
4
Les types ont des tailles spécifiques, cela ne fonctionnera jamais pour les chaînes binaires de longueur arbitraire. Vous pouvez configurer une boucle for pour gérer cela si vous connaissez le type de chaque élément.
Joshua Olson
2
"L" est en fait uint32 (4 octets). Si, comme dans mon cas, vous avez besoin de 8 octets, utilisez "Q" -> uint64. Notez également que "l" -> int32 et q -> int64
ntg
319

Dans Python 3.2 et versions ultérieures, utilisez

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

ou

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

selon l' endianness de votre chaîne d'octets.

Cela fonctionne également pour les entiers de chaînes d'octets de longueur arbitraire, et pour les entiers signés complémentaires à deux en spécifiant signed=True. Consultez la documentation pourfrom_bytes .

Escargot mécanique
la source
@eri combien plus lent? J'avais l'habitude d'utiliser struct mais je l'ai converti en int.from_bytes lorsque je suis allé à py3. J'appelle cette méthode toutes les ms car je reçois des données série, donc toute accélération est la bienvenue. J'ai regardé ceci
Naib
@Naib, pour les os.urandom(4)octets ** 1,4 µs ** (struct) vs ** 2,3 µs ** (int.from_bytes) sur mon processeur. python 3.5.2
eri
5
@eri J'ai ressuscité un script timeit que j'utilisais pour évaluer quelques méthodes CRC. Quatre exécutions 1) struct 2) int.from_bytes 3) comme # 1 mais cython compilé, 4) comme # 2 mais cython compilé. 330ns pour struct, 1.14us pour int (cython a donné une accélération de peut-être 20ns dans les deux ...) on dirait que je reviens :) ce n'est pas une optimisation prématurée, j'ai rencontré des goulots d'étranglement désagréables, en particulier avec un million d'échantillons à publier -process et ont fait tomber des pièces.
Naib
66

Comme Greg l'a dit, vous pouvez utiliser struct si vous avez affaire à des valeurs binaires, mais si vous avez juste un "nombre hexadécimal" mais au format octet, vous voudrez peut-être simplement le convertir comme:

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

... c'est la même chose que:

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

... sauf que cela fonctionnera pour n'importe quel nombre d'octets.

James Antill
la source
3
quelle est exactement la différence entre "valeurs binaires" et "" nombre hexadécimal "mais au format octet" ???????
Voir "structure d'aide". Par exemple. "001122334455" .decode ('hex') ne peut pas être converti en nombre à l'aide de struct.
James Antill
3
En passant, cette réponse suppose que l'entier est encodé dans l'ordre des octets big-endian. Pour l'ordre petit-boutiste, faites:int(''.join(reversed(s)).encode('hex'), 16)
amcnabb
1
bien mais ça va être lent! Je suppose que cela n'a pas vraiment d'importance si vous codez en Python.
MattCochrane
8

J'utilise la fonction suivante pour convertir les données entre int, hex et octets.

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

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

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

Attention: ce qui précède est fortement spécifique à la plateforme. Le spécificateur "I" et l'endianness de la conversion string-> int dépendent de votre implémentation Python particulière. Mais si vous souhaitez convertir plusieurs entiers / chaînes à la fois, le module de tableau le fait rapidement.

Rafał Dowgird
la source
5

Dans Python 2.x, vous pouvez utiliser les spécificateurs de format <Bpour les octets non signés et <bpour les octets signés avec struct.unpack/ struct.pack.

Par exemple:

Soit x='\xff\x10\x11'

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

Et:

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

C'est *nécessaire!

Voir https://docs.python.org/2/library/struct.html#format-characters pour une liste des spécificateurs de format.

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

Test 1: inverse:

>>> hex(2043455163)
'0x79cca6bb'

Test 2: Nombre d'octets> 8:

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

Test 3: incrémenter de un:

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

Test 4: ajoutez un octet, dites «A»:

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

Test 5: Diviser par 256:

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

Le résultat est égal au résultat du test 4, comme prévu.

user3076105
la source
1

J'avais du mal à trouver une solution pour les séquences d'octets de longueur arbitraire qui fonctionneraient sous Python 2.x. Enfin j'ai écrit celui-ci, c'est un peu piraté car il effectue une conversion de chaîne, mais ça marche.

Fonction pour Python 2.x, longueur arbitraire

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)

Cette fonction a deux exigences:

  • L'entrée datadoit être un bytearray. Vous pouvez appeler la fonction comme ceci:

    s = 'y\xcc\xa6\xbb'
    n = signedbytes(s)
  • Les données doivent être big-endian. Si vous avez une valeur petit-boutiste, vous devez d'abord l'inverser:

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

Bien sûr, cela ne doit être utilisé que si une longueur arbitraire est nécessaire. Sinon, tenez-vous-en à des méthodes plus standard (par exemple struct).

Andrea Lazzarotto
la source
1

int.from_bytes est la meilleure solution si vous êtes à la version> = 3.2. La solution "struct.unpack" nécessite une chaîne donc elle ne s'appliquera pas aux tableaux d'octets. Voici une autre solution:

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])) renvoie «0x87654321».

Il gère grand et petit endianness et est facilement modifiable pour 8 octets

user3435121
la source
1

Comme mentionné ci-dessus, l'utilisation de la unpackfonction de struct est un bon moyen. Si vous souhaitez implémenter votre propre fonction, il existe une autre solution:

def bytes_to_int(bytes):
    result = 0
    for b in bytes:
        result = result * 256 + int(b)
return result
abdullahselek
la source
Cela ne fonctionne pas pour un nombre négatif qui a été converti en octets.
Maria
1

En python 3, vous pouvez facilement convertir une chaîne d'octets en une liste d'entiers (0..255) en

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

Une méthode décemment rapide utilisant array.array que j'utilise depuis un certain temps:

variables prédéfinies:

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

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

from int: (écrire)

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

Il est possible que cela soit plus rapide.

EDIT:
Pour certains chiffres, voici un test de performance (Anaconda 2.3.0) montrant des moyennes stables en lecture par rapport à 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%

Ceci est un test de performance brut, donc le pow-flip endian est laissé de côté.
La shiftfonction illustrée applique la même opération de changement de vitesse que la boucle for, et arrest exactement array.array('B',[0,0,255,0])comme elle a les performances itératives les plus rapides à côté de dict.

Je devrais probablement aussi noter que l'efficacité se mesure par la précision au temps moyen.

Tcll
la source