Вывести строку в виде шестнадцатеричных байтов?
У меня есть эта строка: Hello world !!
и я хочу напечатать его, используя Python как 48:65:6c:6c:6f:20:77:6f:72:6c:64:20:21:21
,
hex()
работает только для целых чисел.
Как это можно сделать?
15 ответов
Вы можете преобразовать вашу строку в генератор int, применить шестнадцатеричное форматирование для каждого элемента и вставить его в разделитель:
>>> 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
Для Python 2.x:
':'.join(x.encode('hex') for x in 'Hello World!')
Код выше не будет работать с Python 3.x, для 3.x код ниже будет работать:
':'.join(hex(ord(x))[2:] for x in 'Hello World!')
Другой ответ в две строки, который некоторым может показаться более легким для чтения, помогает при отладке разрывов строк или других нечетных символов в строке:
for character in string:
print character, character.encode('hex')
Некоторые дополнения к ответу Федора Гоголева:
Во-первых, если строка содержит символы, чей "код ASCII" меньше 10, они не будут отображаться должным образом. В этом случае правильный формат должен быть {: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'
^^
Во-вторых, если ваша "строка" на самом деле является "байтовой строкой" - и поскольку различие имеет значение в Python 3 - вы можете предпочесть следующее:
>>> 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'
Обратите внимание, что в приведенном выше коде нет необходимости в преобразовании, поскольку объекты байтов определяются как "неизменяемая последовательность целых чисел в диапазоне 0 <= x <256".
Вывести строку в виде шестнадцатеричных байтов?
Принятый ответ дает:
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'
Принятый ответ работает только до тех пор, пока вы используете байты (в основном символы ascii). Но если вы используете Unicode, например:
a_string = u"Привет мир!!" # "Prevyet mir, or "Hello World" in Russian.
Вам нужно как-то преобразовать в байты.
Если ваш терминал не принимает эти символы, вы можете декодировать из utf-8 или использовать имена (чтобы вы могли вставить и запустить код вместе со мной):
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}"
)
Итак, мы видим, что:
":".join("{:02x}".format(ord(c)) for c in a_string)
возвращается
'41f:440:438:432:435:442:20:43c:438:440:21:21'
плохой / неожиданный результат - это кодовые точки, которые объединяются для создания графем, которые мы видим в юникоде, из консорциума юникод - представляющих языки во всем мире. Это не то, как мы на самом деле храним эту информацию, поэтому она может быть интерпретирована другими источниками.
Чтобы позволить другому источнику использовать эти данные, нам обычно необходимо преобразовать кодировку в utf-8, например, чтобы сохранить эту строку в байтах на диск или опубликовать в html. Так что нам нужна эта кодировка для преобразования кодовых точек в кодовые единицы utf-8 - в Python 3, ord
не нужно, потому что bytes
являются итерациями целых чисел:
>>> ":".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'
Или, возможно, более элегантно, используя новые f-строки (доступно только в 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'
В Python 2 проходят c
в ord
во-первых, т.е. ord(c)
- больше примеров:
>>> ":".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'
Ты можешь использовать hexdump
"s
import hexdump
hexdump.dump("Hello World", sep=":")
(добавить .lower()
если вам требуется строчные буквы). Это работает как для Python 2 и 3.
С помощью функции map и lambda можно получить список шестнадцатеричных значений, которые можно распечатать (или использовать для других целей).
>>> 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']
Немного более общий вариант для тех, кому наплевать на Python3 или двоеточия:
from codecs import encode
data = open('/dev/urandom', 'rb').read(20)
print(encode(data, 'hex')) # data
print(encode(b"hello", 'hex')) # string
Это можно сделать следующими способами:
from __future__ import print_function
str = "Hello World !!"
for char in str:
mm = int(char.encode('hex'), 16)
print(hex(mm), sep=':', end=' ' )
Вывод этого будет в шестнадцатеричном виде следующим образом:
0x48 0x65 0x6c 0x6c 0x6f 0x20 0x57 0x6f 0x72 0x6c 0x64 0x20 0x21 0x21
Для чего-то более производительного, чем ''.format()
, вы можете использовать это:
>>> ':'.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'
>>>
извините, это не могло выглядеть лучше,
было бы хорошо, если бы можно было просто сделать'%02x'%v
, но это требует только int...
но вы застрянете с байтовыми строкамиb''
без логики выбора ord(v)
.
Просто для удобства, очень просто.
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'
В Python 3:
":".join(c.encode().hex() for c in "Hello world !!")
# 48:65:6c:6c:6f:20:77:6f:72:6c:64:20:21:21
Это похоже на ответ @Aesthete.
С помощью base64.b16encode
в python2 (его встроенный)
>>> 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'