web-dev-qa-db-fra.com

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

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

Dis comme ceci: 'y\xcc\xa6\xbb'

Je suis venu avec 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 le fait plus simplement ...

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

MISE À JOUR:

J'aime un peu mieux la réponse de James car elle ne nécessite pas l'importation d'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 de hacky:

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

NOUVELLE MISE À JOUR:

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

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

L'inclusion du coût d'importation du module annule presque tous les avantages de cette méthode. Je pense que cela ne comprendra que les frais d'importation une fois pour toute l'analyse de référence. regardez ce qui se passe quand je le force à 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 exécutez beaucoup de méthodes de cette méthode pour une importation, cela devient proportionnellement moins problématique. C’est aussi probablement le coût des entrées-sorties plutôt que celui des processeurs; il peut donc dépendre de la capacité et des caractéristiques de charge de la machine.

144
ʞɔıu

Vous pouvez aussi utiliser le module struct pour faire ceci:

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

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 le endianness de votre chaîne d'octets.

Cela fonctionne également pour les entiers bytestring de longueur arbitraire et pour les entiers signés en complément de deux en spécifiant signed=True. Voir le docs pour from_bytes .

286
Mechanical snail

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

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 ça fonctionnera pour n'importe quel nombre d'octets.

66
James Antill

J'utilise la fonction suivante pour convertir des 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

7
Jrm
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 la finalité de la conversion chaîne-> int dépendent de votre implémentation Python particulière. Mais si vous voulez convertir plusieurs entiers/chaînes à la fois, le module tableau le fait rapidement.

6
Rafał Dowgird

Dans Python 2.x, vous pouvez utiliser les spécificateurs de format <B pour les octets non signés et <b pour 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'

Ce * est requis!

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

5
Tetralux
>>> 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ément 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.

3
user3076105

int.from_bytes est la meilleure solution si vous êtes à la version> = 3.2. La solution "struct.unpack" nécessite une chaîne afin qu'elle ne s'applique 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 les grandes et petites endianités et est facilement modifiable pour 8 octets.

1
user3435121

Comme mentionné ci-dessus, utiliser unpack la fonction 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
1
abdullahselek

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

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 data doit être une 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 little-endian, vous devez l’inverser en premier:

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

Bien sûr, cela ne devrait être utilisé que si une longueur arbitraire est nécessaire. Sinon, utilisez des méthodes plus standard (par exemple, struct).

1

Une méthode assez 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: (lu)

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

de 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 ceux-ci soient plus rapides.

MODIFIER:
Pour certains chiffres, voici un test de performance (Anaconda 2.3.0) montrant des moyennes stables à la 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 fonction shift présentée applique la même opération de décalage que la boucle for, et arr n’est que array.array('B',[0,0,255,0]) car elle a la performance itérative la plus rapide après dict.

Je devrais probablement aussi noter que l'efficacité est mesurée par la précision au temps moyen.

0
Tcll