Imprimer une chaîne sous forme d'octets hexadécimaux?

155

J'ai cette chaîne: Hello world !!et je veux l'imprimer en utilisant Python comme 48:65:6c:6c:6f:20:77:6f:72:6c:64:20:21:21.

hex() ne fonctionne que pour les entiers.

Comment ceci peut être fait?

Eduard Florinescu
la source
Si l'idée est de ne renvoyer que des valeurs hexadécimales à 2 chiffres, alors cette question implique l'utilisation de chaînes d' octets (c'est-à-dire Python 2 strou Python 3 bytestring), car il n'y a pas de transformation univoque d'un caractère en un entier de 0… 255. Ainsi, les chaînes de caractères (Python 2 unicodeet Python 3 str) nécessitent d'abord un certain encodage avant d'être convertibles dans ce format hexadécimal. La réponse d'Aaron Hall illustre cela.
Eric O Lebigot

Réponses:

227

Vous pouvez transformer votre chaîne en un générateur int, appliquer une mise en forme hexadécimale pour chaque élément et intercaler avec un séparateur:

>>> s = "Hello world !!"
>>> ":".join("{:02x}".format(ord(c)) for c in s)
'48:65:6c:6c:6f:20:77:6f:72:6c:64:20:21:21
Fedor Gogolev
la source
3
Notez que dans python3, le concept d'impression d'un stras hex n'a pas vraiment de sens; vous voudrez imprimer l' bytesobjet en hexadécimal (convertir stren bytesen appelant .encode()).
mic_e
8
En fait, cela produit une sortie invalide dans python3: ":".join("{:02x}".format(ord(c)) for c in 'løl')renvoie '6c:f8:6c', tandis que ":".join("{:02x}".format(c) for c in 'løl'.encode())produit la représentation utf-8 correcte '6c:c3:b8:6c'.
mic_e
2
Cette question et réponse suppose en quelque sorte que votre entrée ne contient jamais de caractères non ASCII. Si votre entrée peut contenir des éléments tels que des émoticônes ou des systèmes d'écriture non latins, vous voudrez peut-être utiliser ":".join("{:04x}".format(ord(c)) for c in s)(en remplaçant 02xpar un 04xremplissage à zéro de chaque numéro pour qu'il soit composé de 4 chiffres) à la place
Boris
@mic_e Pourquoi est-ce? Scapy fait référence à cela lorsque vous l'essayez dans l'interpréteur intégré. WARNING: Calling str(pkt) on Python 3 makes no sense!
sherrellbc
157
':'.join(x.encode('hex') for x in 'Hello World!')
Esthète
la source
3
Comment faire cela en python3?
h__
6
@hyh: h = binascii.hexlify(b"Hello world !!") to get hex string. b":".join(h[i:i+2] for i in range(0, len(h), 2))pour y insérer ':'tous les deux chiffres hexadécimaux.
jfs
2
Ne fonctionne pas sur Python 3.LookupError: 'hex' is not a text encoding; use codecs.encode() to handle arbitrary codecs
Boris
55

Pour Python 2.x:

':'.join(x.encode('hex') for x in 'Hello World!')

Le code ci-dessus ne fonctionnera pas avec Python 3.x , pour 3.x, le code ci-dessous fonctionnera:

':'.join(hex(ord(x))[2:] for x in 'Hello World!')
Kelvin Hu
la source
1
il convient également de noter que ce dernier fonctionnera ÉGALEMENT avec python2.x ET qu'il fonctionnera également pour les caractères non-ascii
raudi
1
Mais notez aussi que ce dernier ne remplit pas les zéros non significatifs: hex (ord ("\ x00")) [2:] vaut "0" et "\ x00" .encode ("hex") == "00"
Will Daniels
3
Pourquoi avez-vous décidé de publier ceci comme une nouvelle réponse, des mois après que ces deux solutions aient été proposées par d'autres utilisateurs? S'il s'agissait de clarifier la compatibilité des versions, il aurait été plus logique de proposer des modifications aux réponses existantes.
Air le
2
Comme indiqué ailleurs, cette réponse n'est même pas correcte une fois que l'on dépasse l'ascii et considère l'unicode. ':'. join (hex (ord (x)) [2:] for x in 'løl') imprime incorrectement '6c: f8: 6c' alors que la sortie correcte est '6c: c3: b8: 6c'.
mcduffee
23

Une autre réponse en deux lignes que certains pourraient trouver plus facile à lire et qui aide au débogage des sauts de ligne ou d'autres caractères impairs dans une chaîne:

Pour Python 2.7

for character in string:
    print character, character.encode('hex')

Pour Python 3.7 (non testé sur toutes les versions de 3)

for character in string:
    print(character, character.encode('utf-8').hex())
copeland3300
la source
Cela ne fonctionne pas depuis Python 3.6.8 (au moins): "hex" n'est pas un encodage de chaînes. codecs.encode(<bytestring>, "hex")fonctionne, cependant.
Eric O Lebigot
2
Ah, merci pour l'info ... oui, cela a été définitivement écrit pour Python 2.7. Je vais mettre à jour ma réponse pour inclure comment le faire pour Python 3.7.
copeland3300
Vérifié, Python 3.7.6: import sys; s="Déjà vu Besançon,Lupiñén,Šiauliai,Großräschen,Łódź,Аша,广东省,LA"; for c in s:; w=sys.stdout.write(c+":"+c.encode('utf-8').hex()+"||"); (sort)D:44||é:c3a9||j:6a||à:c3a0|| :20||v:76||u:75|| :20||B:42||e:65||s:73||a:61||n:6e||ç:c3a7||o:6f||n:6e||,:2c||L:4c||u:75||p:70||i:69||ñ:c3b1||é:c3a9||n:6e||,:2c||Š:c5a0||i:69||a:61||u:75||l:6c||i:69||a:61||i:69||,:2c||G:47||r:72||o:6f||ß:c39f||r:72||ä:c3a4||s:73||c:63||h:68||e:65||n:6e||,:2c||Ł:c581||ó:c3b3||d:64||ź:c5ba||,:2c||А:d090||ш:d188||а:d0b0||,:2c||广:e5b9bf||东:e4b89c||省:e79c81||,:2c||L:4c||A:41||
bballdave025
20

Quelques compléments à Fedor Gogolev répondent:

Premièrement, si la chaîne contient des caractères dont le «code ASCII» est inférieur à 10, ils ne seront pas affichés comme requis. Dans ce cas, le format correct doit être {:02x}:

>>> s = "Hello unicode \u0005 !!"
>>> ":".join("{0:x}".format(ord(c)) for c in s)
'48:65:6c:6c:6f:20:75:6e:69:63:6f:64:65:20:5:20:21:21'
                                           ^

>>> ":".join("{:02x}".format(ord(c)) for c in s)
'48:65:6c:6c:6f:20:75:6e:69:63:6f:64:65:20:05:20:21:21'
                                           ^^

Deuxièmement, si votre "chaîne" est en réalité une "chaîne d'octets" - et puisque la différence compte dans Python 3 - vous préférerez peut-être ce qui suit:

>>> s = b"Hello bytes \x05 !!"
>>> ":".join("{:02x}".format(c) for c in s)
'48:65:6c:6c:6f:20:62:79:74:65:73:20:05:20:21:21'

Veuillez noter qu'il n'y a pas besoin de conversion dans le code ci-dessus car un objet bytes est défini comme "une séquence immuable d'entiers dans la plage 0 <= x <256" .

Sylvain Leroux
la source
11

Imprimer une chaîne sous forme d'octets hexadécimaux?

La réponse acceptée donne:

s = "Hello world !!"
":".join("{:02x}".format(ord(c)) for c in s)

Retour:

'48:65:6c:6c:6f:20:77:6f:72:6c:64:20:21:21'

La réponse acceptée ne fonctionne que tant que vous utilisez des octets (principalement des caractères ascii). Mais si vous utilisez unicode, par exemple:

a_string = u"Привет мир!!" # "Prevyet mir", or "Hello World" in Russian.

Vous devez d'une manière ou d'une autre convertir en octets.

Si votre terminal n'accepte pas ces caractères, vous pouvez décoder à partir de UTF-8 ou utiliser les noms (vous pouvez donc coller et exécuter le code avec moi):

a_string = (
    "\N{CYRILLIC CAPITAL LETTER PE}"
    "\N{CYRILLIC SMALL LETTER ER}"
    "\N{CYRILLIC SMALL LETTER I}"
    "\N{CYRILLIC SMALL LETTER VE}"
    "\N{CYRILLIC SMALL LETTER IE}"
    "\N{CYRILLIC SMALL LETTER TE}"
    "\N{SPACE}"
    "\N{CYRILLIC SMALL LETTER EM}"
    "\N{CYRILLIC SMALL LETTER I}"
    "\N{CYRILLIC SMALL LETTER ER}"
    "\N{EXCLAMATION MARK}"
    "\N{EXCLAMATION MARK}"
)

Nous voyons donc que:

":".join("{:02x}".format(ord(c)) for c in a_string)

Retour

'41f:440:438:432:435:442:20:43c:438:440:21:21'

un résultat médiocre / inattendu - ce sont les points de code qui se combinent pour former les graphèmes que nous voyons dans Unicode, du consortium Unicode - représentant des langues du monde entier. Ce n'est pas ainsi que nous stockons ces informations afin qu'elles puissent être interprétées par d'autres sources, cependant.

Pour permettre à une autre source d'utiliser ces données, nous aurions généralement besoin de convertir en encodage UTF-8, par exemple, pour enregistrer cette chaîne en octets sur le disque ou pour la publier en html. Nous avons donc besoin de cet encodage pour convertir les points de code en unités de code de UTF-8 - en Python 3, ordn'est pas nécessaire car il bytesexiste des itérables d'entiers:

>>> ":".join("{:02x}".format(c) for c in a_string.encode('utf-8'))
'd0:9f:d1:80:d0:b8:d0:b2:d0:b5:d1:82:20:d0:bc:d0:b8:d1:80:21:21'

Ou peut-être plus élégamment, en utilisant les nouvelles f-strings (uniquement disponibles en Python 3):

>>> ":".join(f'{c:02x}' for c in a_string.encode('utf-8'))
'd0:9f:d1:80:d0:b8:d0:b2:d0:b5:d1:82:20:d0:bc:d0:b8:d1:80:21:21'

En Python 2, passez cau ordpremier, c'est ord(c)-à- dire - plus d'exemples:

>>> ":".join("{:02x}".format(ord(c)) for c in a_string.encode('utf-8'))
'd0:9f:d1:80:d0:b8:d0:b2:d0:b5:d1:82:20:d0:bc:d0:b8:d1:80:21:21'
>>> ":".join(format(ord(c), '02x') for c in a_string.encode('utf-8'))
'd0:9f:d1:80:d0:b8:d0:b2:d0:b5:d1:82:20:d0:bc:d0:b8:d1:80:21:21'
Salle Aaron
la source
1
@ not2qubit veuillez réessayer ces exemples - j'ai pris un peu de temps pour aborder les différences entre Python 2 et 3, et apparemment, je les ai initialement écrits uniquement pour Python 2. Et merci d'avoir QA'ing ma réponse!
Aaron Hall
Oui, ça l'a fait. Je vous remercie!
not2qubit
8

Vous pouvez utiliser hexdumples

import hexdump
hexdump.dump("Hello World", sep=":")

(ajoutez .lower()si vous avez besoin de minuscules). Cela fonctionne pour Python 2 et 3.

Tobias Kienzler
la source
Aussi un problème que j'ai rencontré, si vous avez des problèmes pour installer hexdump ou tout autre package, c'est généralement à cause des paramètres du proxy, essayez d'exécuter pip avec l'option proxy pip install -U hexdump --proxy http://proxy.address:port
Eduard Florinescu
En fait, j'ai fait l'erreur d'utiliser sudoavec pip, ce qui a gâché pacman...
Tobias Kienzler
6

L'utilisation de la carte et de la fonction lambda peut produire une liste de valeurs hexadécimales, qui peuvent être imprimées (ou utilisées à d'autres fins)

>>> s = 'Hello 1 2 3 \x01\x02\x03 :)'

>>> map(lambda c: hex(ord(c)), s)
['0x48', '0x65', '0x6c', '0x6c', '0x6f', '0x20', '0x31', '0x20', '0x32', '0x20', '0x33', '0x20', '0x1', '0x2', '0x3', '0x20', '0x3a', '0x29']
BrendanSimon
la source
[hex(ord(c)) for c in s]
Boris
2

Cela peut être fait des manières suivantes:

from __future__ import print_function
str = "Hello World !!"
for char in str:
    mm = int(char.encode('hex'), 16)
    print(hex(mm), sep=':', end=' ' )

La sortie de ceci sera en hexadécimal comme suit:

0x48 0x65 0x6c 0x6c 0x6f 0x20 0x57 0x6f 0x72 0x6c 0x64 0x20 0x21 0x21

Ghansham
la source
où puis-je trouver le futur
tofutim
Pour référence future, __future__est une bibliothèque standard disponible dans les versions récentes de Python 2 qui peut être utilisée pour rendre les fonctionnalités normalement uniquement dans Python 3 rétrocompatibles. Dans cette réponse, il est utilisé pour obtenir la print(text)fonction "fonction d'impression", qui remplace la print textsyntaxe de Python 2. Voir la documentation Python .
Eric Reed
2

Un peu plus général pour ceux qui ne se soucient pas de Python3 ou des deux-points:

from codecs import encode

data = open('/dev/urandom', 'rb').read(20)
print(encode(data, 'hex'))      # data

print(encode(b"hello", 'hex'))  # string
Gringo Suave
la source
0

Utilisation base64.b16encodeen python2 (son intégré)

>>> s = 'Hello world !!'
>>> h = base64.b16encode(s)
>>> ':'.join([h[i:i+2] for i in xrange(0, len(h), 2)]
'48:65:6C:6C:6F:20:77:6F:72:6C:64:20:21:21'
Shiplu Mokaddim
la source
Cela ne marche pas. Qu'utilisez-vous pour l'importation et pourquoi ne pas l'utiliser .decode()?
not2qubit
0

Juste pour plus de commodité, très simple.

def hexlify_byteString(byteString, delim="%"):
    ''' very simple way to hexlify a bytestring using delimiters '''
    retval = ""
    for intval in byteString:
        retval += ( '0123456789ABCDEF'[int(intval / 16)])
        retval += ( '0123456789ABCDEF'[int(intval % 16)])
        retval += delim
    return( retval[:-1])

hexlify_byteString(b'Hello World!', ":")
# Out[439]: '48:65:6C:6C:6F:20:57:6F:72:6C:64:21'
BerndSchmitt
la source
0

pour quelque chose qui offre plus de performances que ''.format(), vous pouvez utiliser ceci:

>>> ':'.join( '%02x'%(v if type(v) is int else ord(v)) for v in 'Hello World !!' )
'48:65:6C:6C:6F:20:77:6F:72:6C:64:20:21:21'
>>> 
>>> ':'.join( '%02x'%(v if type(v) is int else ord(v)) for v in b'Hello World !!' )
'48:65:6C:6C:6F:20:77:6F:72:6C:64:20:21:21'
>>> 

désolé, cela ne pourrait pas être plus
beau, ce serait bien si on pouvait simplement le faire '%02x'%v, mais cela ne prend que des int ...
mais vous serez coincé avec des chaînes d'octets b''sans logique de sélection ord(v).

Tcll
la source