Imprimir una cadena como bytes hexadecimales?

155

Tengo esta cadena: Hello world !!y quiero imprimirla usando Python como 48:65:6c:6c:6f:20:77:6f:72:6c:64:20:21:21.

hex() funciona solo para enteros.

¿Cómo puede hacerse esto?

Eduard Florinescu
fuente
Si la idea es devolver solo valores hexadecimales de 2 dígitos, entonces esta pregunta implica el uso de cadenas de bytes (es decir, Python 2 stro Python 3 bytestring), ya que no hay transformación inequívoca de un personaje en un entero en 0 ... 255. Por lo tanto, las cadenas de caracteres (Python 2 unicodey Python 3 str) primero requieren algo de codificación antes de ser convertibles en este formato hexadecimal. La respuesta de Aaron Hall ejemplifica esto.
Eric O Lebigot

Respuestas:

227

Puede transformar su cadena en un generador int, aplicar formato hexadecimal para cada elemento e intercalar con separador:

>>> 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
fuente
3
Tenga en cuenta que en python3, el concepto de imprimir un strcomo hexadecimal realmente no tiene sentido; querrá imprimir el bytesobjeto como hexadecimal (convertir stra bytesllamando .encode()).
mic_e
8
De hecho, esto produce una salida no válida en python3: ":".join("{:02x}".format(ord(c)) for c in 'løl')devuelve '6c:f8:6c', mientras que ":".join("{:02x}".format(c) for c in 'løl'.encode())produce la representación utf-8 correcta '6c:c3:b8:6c'.
mic_e
2
Esta pregunta y respuesta suponen que su entrada nunca contiene caracteres que no sean ASCII. Si su entrada puede contener elementos como emojis o sistemas de escritura no basados ​​en latín, es posible que desee usar ":".join("{:04x}".format(ord(c)) for c in s)(reemplazando 02xcon 04xcero cada número para que tenga 4 dígitos)
Boris
@mic_e ¿Por qué es esto? Scapy hace referencia a esto cuando lo prueba en el intérprete incorporado. WARNING: Calling str(pkt) on Python 3 makes no sense!
sherrellbc
157
':'.join(x.encode('hex') for x in 'Hello World!')
Esteta
fuente
3
¿Cómo hacer esto en python3?
h__
66
@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))para insertar ':'después de cada dos dígitos hexadecimales.
jfs
2
No funciona en Python 3.LookupError: 'hex' is not a text encoding; use codecs.encode() to handle arbitrary codecs
Boris
55

Para Python 2.x:

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

El código anterior no funcionará con Python 3.x , para 3.x, el código siguiente funcionará:

':'.join(hex(ord(x))[2:] for x in 'Hello World!')
Kelvin Hu
fuente
1
También debe tenerse en cuenta que el TAMBIÉN funcionará también con python2.x Y también funcionará para caracteres no
ASCII
1
Pero también tenga en cuenta que este último no rellena los ceros iniciales: hex (ord ("\ x00")) [2:] es "0" y "\ x00" .encode ("hex") == "00"
Will Daniels
3
¿Por qué decidió publicar esto como una nueva respuesta, meses después de que ambas soluciones hubieran sido ofrecidas por otros usuarios? Si el punto fuera aclarar la compatibilidad de la versión, habría tenido más sentido sugerir modificaciones a las respuestas existentes.
Air
2
Como se señaló en otra parte, esta respuesta ni siquiera es correcta una vez que uno se mueve más allá de ascii y considera unicode. ':'. join (hex (ord (x)) [2:] para x en 'løl') imprime incorrectamente '6c: f8: 6c' mientras que la salida correcta es '6c: c3: b8: 6c'.
mcduffee
23

Otra respuesta en dos líneas que algunos pueden encontrar más fácil de leer y ayuda a depurar saltos de línea u otros caracteres extraños en una cadena:

Para Python 2.7

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

Para Python 3.7 (no probado en todas las versiones de 3)

for character in string:
    print(character, character.encode('utf-8').hex())
copeland3300
fuente
Esto no funciona a partir de Python 3.6.8 (al menos): "hex" no es una codificación de cadenas. codecs.encode(<bytestring>, "hex")funciona, sin embargo.
Eric O Lebigot
2
Ah, gracias por la información ... sí, esto definitivamente fue escrito para Python 2.7. Actualizaré mi respuesta para incluir cómo hacerlo para Python 3.7.
copeland3300
Verified, 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()+"||"); (out)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

Algunos complementos a la respuesta de Fedor Gogolev:

Primero, si la cadena contiene caracteres cuyo 'código ASCII' es inferior a 10, no se mostrarán según sea necesario. En ese caso, el formato correcto debería ser {: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'
                                           ^^

En segundo lugar, si su "cadena" es en realidad una "cadena de bytes", y dado que la diferencia es importante en Python 3, puede preferir lo siguiente:

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

Tenga en cuenta que no hay necesidad de conversión en el código anterior ya que los objetos de bytes se definen como "una secuencia inmutable de enteros en el rango 0 <= x <256" .

Sylvain Leroux
fuente
11

Imprimir una cadena como bytes hexadecimales?

La respuesta aceptada da:

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

devoluciones:

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

La respuesta aceptada solo funciona mientras use bytes (principalmente caracteres ascii). Pero si usa unicode, por ejemplo:

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

Necesita convertir a bytes de alguna manera.

Si su terminal no acepta estos caracteres, puede decodificar desde UTF-8 o usar los nombres (para que pueda pegar y ejecutar el código junto conmigo):

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}"
)

Entonces vemos que:

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

devoluciones

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

un resultado pobre / inesperado: estos son los puntos de código que se combinan para hacer que los grafemas que vemos en Unicode, del Consorcio Unicode, representen idiomas en todo el mundo. Sin embargo, no es así como almacenamos esta información para que otras fuentes puedan interpretarla.

Para permitir que otra fuente use estos datos, generalmente deberíamos convertir a la codificación UTF-8, por ejemplo, para guardar esta cadena en bytes en el disco o para publicar en html. Por lo tanto, necesitamos esa codificación para convertir los puntos de código en las unidades de código de UTF-8; en Python 3, ordno es necesaria porque bytesson iterables de enteros:

>>> ":".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'

O quizás de manera más elegante, utilizando las nuevas cadenas f (solo 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, pase cal ordprimero, es decir ord(c), más ejemplos:

>>> ":".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'
Aaron Hall
fuente
1
@ not2qubit, intente estos ejemplos nuevamente. Me tomé un poco de tiempo para abordar las diferencias entre Python 2 y 3, y aparentemente originalmente solo escribí esto para Python 2. ¡Y gracias por responder mi respuesta!
Aaron Hall
Si, eso lo hizo. ¡Gracias!
not2qubit el
8

Puedes usar hexdump's

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

(agregue .lower()si necesita minúsculas). Esto funciona tanto para Python 2 como para 3.

Tobias Kienzler
fuente
También tuve un problema, si tiene problemas para instalar hexdump o cualquier otro paquete, generalmente debido a la configuración del proxy, intente ejecutar pip con la opción de proxy pip install -U hexdump --proxy http://proxy.address:port
Eduard Florinescu
En realidad me hizo el error de usar sudocon pip, que en mal estado pacman...
Tobias Kienzler
6

El uso de la función map y lambda puede producir una lista de valores hexadecimales, que pueden imprimirse (o usarse para otros fines)

>>> 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
fuente
[hex(ord(c)) for c in s]
Boris
2

Esto se puede hacer de las siguientes maneras:

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

La salida de esto estará en hexadecimal de la siguiente manera:

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

Ghansham
fuente
donde encuentro el futuro
tofutim
Para referencia futura, __future__es una biblioteca estándar disponible en versiones recientes de Python 2 que se puede usar para hacer que las características normalmente solo sean compatibles con Python 3. En esta respuesta, se utiliza para obtener la print(text)función "función de impresión", que reemplaza la print textsintaxis de Python 2. Consulte los documentos de Python .
Eric Reed
2

Un poco más general para aquellos que no se preocupan por Python3 o dos puntos:

from codecs import encode

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

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

Utilizando base64.b16encodeen python2 (su incorporado)

>>> 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
fuente
Esto no funciona ¿Qué estás usando para importar y por qué no usar .decode()?
not2qubit el
0

Solo por conveniencia, muy 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
fuente
0

para algo que ofrece más rendimiento que ''.format(), puede usar esto:

>>> ':'.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'
>>> 

lo siento, esto no podría verse mejor
sería bueno si uno simplemente pudiera hacerlo '%02x'%v, pero eso solo requiere int ...
pero estará atrapado con cadenas de bytes b''sin la lógica para seleccionar ord(v).

Tcll
fuente