179

I have this string: Hello, World! and I want to print it using Python as '48:65:6c:6c:6f:2c:20:57:6f:72:6c:64:21'.

hex() works only for integers.

How can it be done?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Eduard Florinescu
  • 16,747
  • 28
  • 113
  • 179
  • 2
    If the idea is to return only 2-digit hex values, then this question implies the use of _byte_ strings (i.e. Python 2 `str` or Python 3 `bytestring`), as there is no unequivocal transformation of a character into an integer in 0…255. Thus, character strings (Python 2 `unicode` and Python 3 `str`) first require some encoding before being convertible in this hexadecimal format. Aaron Hall's answer exemplifies this. – Eric O. Lebigot Dec 18 '19 at 14:10

13 Answers13

258

You can transform your string to an integer generator. Apply hexadecimal formatting for each element and intercalate with a separator:

>>> s = "Hello, World!"
>>> ":".join("{:02x}".format(ord(c)) for c in s)
'48:65:6c:6c:6f:2c:20:57:6f:72:6c:64:21
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Fedor Gogolev
  • 10,391
  • 4
  • 30
  • 36
  • 3
    Note that in python3, the concept of printing a `str` as hex doesn't really make sense; you'll want to print `bytes` object as hex (convert `str` to `bytes` by calling `.encode()`). – mic_e May 08 '15 at 12:53
  • 10
    In fact, this produces invalid output in python3: `":".join("{:02x}".format(ord(c)) for c in 'løl')` returns `'6c:f8:6c'`, while `":".join("{:02x}".format(c) for c in 'løl'.encode())` produces the correct utf-8 representation `'6c:c3:b8:6c'`. – mic_e May 08 '15 at 12:58
  • 3
    This question and answer sort of assume that your input never contains non-ASCII characters. If your input might contain things like emojis or non-Latin based writting systems, you might want to use `":".join("{:04x}".format(ord(c)) for c in s)` (replacing `02x` with `04x` to zero-pad each number to be 4 digits) instead – Boris Verkhovskiy Nov 11 '19 at 02:30
  • @mic_e Why is this? Scapy makes a reference to this when you try it in the embedded interpreter. `WARNING: Calling str(pkt) on Python 3 makes no sense!` – sherrellbc Jul 20 '20 at 14:10
  • 1
    @sherrellbc In `python2` strings are sequences of bytes; `ord` gives a value between 0 and `0xff`. In `python3` strings are sequences of unicode codepoints. `ord` gives the number of the codepoint, which can be up to `0x10ffff`. In `python3` you want to use the `bytes` type, which you can get by encoding a string with `.encode()`. – mic_e Jul 27 '20 at 12:10
162
':'.join(x.encode('hex') for x in 'Hello, World!')
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Aesthete
  • 18,622
  • 6
  • 36
  • 45
  • 5
    How to do this in python3? – h__ Apr 12 '13 at 02:10
  • 9
    @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))` to insert `':'` after every two hex digits in it. – jfs Mar 12 '14 at 01:17
  • 6
    Doesn't work on Python 3. `LookupError: 'hex' is not a text encoding; use codecs.encode() to handle arbitrary codecs` – Boris Verkhovskiy Nov 11 '19 at 02:23
60

For Python 2.x:

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

The code above will not work with Python 3.x. For 3.x, the code below will work:

':'.join(hex(ord(x))[2:] for x in 'Hello, World!')
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kelvin Hu
  • 1,299
  • 16
  • 31
  • 1
    it should also be noted, that the later will ALSO work with python2.x AND it will also work for non-ascii characters – raudi Feb 28 '13 at 10:24
  • 3
    But also note that the latter does not pad leading zeros: hex(ord("\x00"))[2:] is "0" and "\x00".encode("hex") == "00" – Will Daniels May 10 '13 at 13:24
  • 4
    Why did you decide to post this as a new answer, months after both of these solutions had been offered by other users? If the point was to clarify version compatibility, it would have made more sense to suggest edits to the existing answers. – Air Jun 04 '14 at 18:25
  • 2
    As noted elsewhere, this answer is not even correct once one moves beyond ascii and considers unicode. ':'.join(hex(ord(x))[2:] for x in 'løl') incorrectly prints '6c:f8:6c' while the correct output is '6c:c3:b8:6c'. – mcduffee May 21 '17 at 18:58
  • @mcduffee, ø is 0xF8 as a [simple search](https://www.google.com/search?q=%C3%B8+unicode) would show, thus '6c:f8:6c' is correct. – Hans Deragon Mar 18 '23 at 00:58
  • Here is a version which pads the hex value properly if under 0x10 and renders everything in upper case: `':'.join("{:0>2}".format(hex(ord(x))[2:]) for x in 'Hello,\nWorld!').upper()` – Hans Deragon Mar 18 '23 at 01:00
26

Another answer in two lines that some might find easier to read, and helps with debugging line breaks or other odd characters in a string:

For Python 2.7

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

For Python 3.7 (not tested on all releases of 3)

for character in string:
    print(character, character.encode('utf-8').hex())
copeland3300
  • 581
  • 7
  • 11
  • 1
    This doesn't work as of Python 3.6.8 (at least): "hex" is not an encoding of strings. `codecs.encode(, "hex")` does work, though. – Eric O. Lebigot Dec 18 '19 at 14:02
  • 2
    Ah, nice thanks for the info...yeah this was definitely written for Python 2.7. I'll update my answer to include how to do it for Python 3.7. – copeland3300 Dec 19 '19 at 21:51
  • 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 Dec 26 '19 at 21:52
22

Some complements to Fedor Gogolev's answer:

First, if the string contains characters whose ASCII code is below 10, they will not be displayed as required. In that case, the correct format should be {: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: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:21:21'
                                           ^^

Second, if your "string" is in reality a "byte string" -- and since the difference matters in Python 3 -- you might prefer the following:

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

Please note there is no need for conversion in the above code as a bytes object is defined as "an immutable sequence of integers in the range 0 <= x < 256".

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sylvain Leroux
  • 50,096
  • 7
  • 103
  • 125
14

Print a string as hex bytes?

The accepted answer gives:

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

returns:

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

The accepted answer works only so long as you use bytes (mostly ascii characters). But if you use unicode, e.g.:

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

You need to convert to bytes somehow.

If your terminal doesn't accept these characters, you can decode from UTF-8 or use the names (so you can paste and run the code along with me):

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

So we see that:

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

returns

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

a poor/unexpected result - these are the code points that combine to make the graphemes we see in Unicode, from the Unicode Consortium - representing languages all over the world. This is not how we actually store this information so it can be interpreted by other sources, though.

To allow another source to use this data, we would usually need to convert to UTF-8 encoding, for example, to save this string in bytes to disk or to publish to html. So we need that encoding to convert the code points to the code units of UTF-8 - in Python 3, ord is not needed because bytes are iterables of integers:

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

Or perhaps more elegantly, using the new f-strings (only available in 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'

In Python 2, pass c to ord first, i.e. ord(c) - more examples:

>>> ":".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'
Eric Reed
  • 377
  • 1
  • 6
  • 21
Russia Must Remove Putin
  • 374,368
  • 89
  • 403
  • 331
8

You can use hexdump's:

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

(append .lower() if you require lower-case). This works for both Python 2 and 3.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Tobias Kienzler
  • 25,759
  • 22
  • 127
  • 221
  • Also a problem I ran into, if you have problems installing hexdump or any other package it is usualy because of the proxy settings try run pip with the proxy option `pip install -U hexdump --proxy http://proxy.address:port` – Eduard Florinescu Oct 13 '15 at 17:28
  • Actually I made the mistake of using `sudo` with `pip`, which messed up `pacman`... – Tobias Kienzler Oct 13 '15 at 17:31
  • Using 3rd party libraries because of such a simple task is overkill. – Fusion Jun 13 '22 at 11:24
6

Using map and lambda function can produce a list of hex values, which can be printed (or used for other purposes)

>>> 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
  • 665
  • 1
  • 9
  • 23
4

A bit more general for those who don't care about Python 3 or colons:

from codecs import encode

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

print(encode(b"hello", 'hex'))  # String
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Gringo Suave
  • 29,931
  • 6
  • 88
  • 75
3

With f-string:

"".join(f"{ord(c):x}" for c in "Hello")

Use any delimiter:

>>> "⚡".join(f"{ord(c):x}" for c in "Hello")
'48⚡65⚡6c⚡6c⚡6f'
Sean
  • 1,055
  • 11
  • 10
2

This can be done in the following ways:

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

The output of this will be in hexadecimal as follows:

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ghansham
  • 448
  • 5
  • 19
  • where do i find the __future__ – tofutim Mar 29 '18 at 22:13
  • For future reference, `__future__` is a standard library available in recent versions of Python 2 that can be used to make features normally only in Python 3 backwards-compatible. In this answer, it's used to get the `print(text)` "print function" feature, which replaces the `print text` syntax from Python 2. See [the Python docs](https://docs.python.org/2/library/__future__.html). – Eric Reed May 12 '19 at 16:14
  • This doesn't work on Python 3 (tried on Python 3.8.5 under Linux ([Ubuntu MATE 20.04](https://en.wikipedia.org/wiki/Ubuntu_MATE#Releases) (Focal Fossa)): `LookupError: 'hex' is not a text encoding; use codecs.encode() to handle arbitrary codecs` – Peter Mortensen Apr 26 '21 at 11:04
2

For something that offers more performance than ''.format(), you can use this:

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

I am sorry this couldn't look nicer.

It would be nice if one could simply do '%02x'%v, but that only takes int...

But you'll be stuck with byte-strings b'' without the logic to select ord(v).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Tcll
  • 7,140
  • 1
  • 20
  • 23
1

Just for convenience, very simple.

def hexlify_byteString(byteString, delim="%"):
    ''' Very simple way to hexlify a byte string 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:2C:20:57:6F:72:6C:64:21'
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
BerndSchmitt
  • 384
  • 3
  • 8