Вывести строку в виде шестнадцатеричных байтов?

155

У меня есть эта строка: Hello world !!и я хочу напечатать ее, используя Python как48:65:6c:6c:6f:20:77:6f:72:6c:64:20:21:21 .

hex() работает только для целых чисел.

Как это можно сделать?

Эдуард Флоринеску
источник
Если идея состоит в том, чтобы возвращать только двухзначные шестнадцатеричные значения, то этот вопрос подразумевает использование байтовых строк (т. Е. Python 2 strили Python 3 bytestring), поскольку нет однозначного преобразования символа в целое число в 0 ... 255. Таким образом, строки символов (Python 2 unicodeи Python 3 str) сначала требуют некоторой кодировки, прежде чем они будут преобразованы в этот шестнадцатеричный формат. Ответ Аарона Холла иллюстрирует это.
Эрик О Лебиго

Ответы:

227

Вы можете преобразовать вашу строку в генератор 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
Федор Гоголев
источник
3
Обратите внимание, что в python3 концепция печати в strвиде гекса не имеет смысла; Вы хотите напечатать bytesобъект как шестнадцатеричный (преобразовать strв bytesвызов .encode()).
mic_e
8
На самом деле, это производит недопустимый выход в Python3: ":".join("{:02x}".format(ord(c)) for c in 'løl')возвращается '6c:f8:6c', в то время как ":".join("{:02x}".format(c) for c in 'løl'.encode())дает правильное представление UTF-8 '6c:c3:b8:6c'.
mic_e
2
Этот вопрос и ответ вроде бы предполагают, что ваш ввод никогда не содержит не-ASCII символов. Если вход может содержать такие вещи , как смайлики или нелатинские системы на основе сочинительства, вы можете захотеть использовать ":".join("{:04x}".format(ord(c)) for c in s)(заменяющий 02xс 04xк нулевой площадке каждому номера из 4 цифр) вместо
Boris
@mic_e Почему это? Scapy ссылается на это, когда вы пробуете его во встроенном интерпретаторе. WARNING: Calling str(pkt) on Python 3 makes no sense!
sherrellbc
157
':'.join(x.encode('hex') for x in 'Hello World!')
Эстет
источник
3
Как это сделать в 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))вставлять ':'после каждых двух шестнадцатеричных цифр в нем.
Jfs
2
Не работает на Python 3.LookupError: 'hex' is not a text encoding; use codecs.encode() to handle arbitrary codecs
Борис
55

Для 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!')
Кельвин Ху
источник
1
Также следует отметить, что позднее также будет работать с python2.x И он также будет работать для не-ASCII символов
Рауди
1
Но также обратите внимание, что последний не дополняет начальные нули: hex (ord ("\ x00")) [2:] равен "0" и "\ x00" .encode ("hex") == "00"
Уилл Дэниелс
3
Почему вы решили опубликовать это как новый ответ, спустя месяцы после того, как оба эти решения были предложены другими пользователями? Если бы цель состояла в том, чтобы уточнить совместимость версий, было бы более разумно предлагать изменения к существующим ответам.
Air
2
Как отмечалось в другом месте, этот ответ даже не является правильным, если человек выходит за рамки ascii и рассматривает юникод. «:». join (hex (ord (x)) [2:] для x в «løl») неправильно печатает «6c: f8: 6c», в то время как правильный вывод «6c: c3: b8: 6c».
McDuffee
23

Другой ответ в две строки, который некоторым может показаться более легким для чтения, помогает при отладке разрывов строк или других нечетных символов в строке:

Для Python 2.7

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

Для Python 3.7 (не тестируется на всех выпусках 3)

for character in string:
    print(character, character.encode('utf-8').hex())
copeland3300
источник
Это не работает с Python 3.6.8 (по крайней мере): «hex» не является кодировкой строк. codecs.encode(<bytestring>, "hex")работает, хотя.
Эрик О Лебиго
2
Ах, спасибо за информацию ... да, это определенно написано для Python 2.7. Я обновлю свой ответ, чтобы включить, как это сделать для Python 3.7.
copeland3300
Подтверждено, 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()+"||"); (ушел)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

Некоторые дополнения к ответу Федора Гоголева:

Во-первых, если строка содержит символы, чей «код 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» .

Сильвен Леру
источник
11

Вывести строку в виде шестнадцатеричных байтов?

Принятый ответ дает:

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'

плохой / неожиданный результат - это те кодовые точки, которые объединяются для создания графем, которые мы видим в Unicode, от Консорциума Unicode - представляющих языки во всем мире. Это не то, как мы на самом деле храним эту информацию, поэтому она может быть интерпретирована другими источниками.

Чтобы позволить другому источнику использовать эти данные, нам обычно необходимо преобразовать кодировку в 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'
Аарон Холл
источник
1
@ not2qubit, пожалуйста, попробуйте эти примеры еще раз - я потратил немного времени на устранение различий между Python 2 и 3, и, очевидно, я изначально написал их только для Python 2. И спасибо за QA'ing мой ответ!
Аарон Холл
Да, это сделал. Спасибо!
not2qubit
8

Вы можете использовать hexdump«S

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

(добавьте, .lower()если вам требуется строчные буквы). Это работает как для Python 2 и 3.

Тобиас Кинцлер
источник
Также я столкнулся с проблемой, если у вас есть проблемы с установкой hexdump или любого другого пакета, это обычно из-за настроек прокси, попробуйте запустить pip с опцией прокси pip install -U hexdump --proxy http://proxy.address:port
Eduard Florinescu
На самом деле я допустил ошибку использования sudoс pip, что испортило pacman...
Тобиас Кинцлер
6

С помощью функции 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']
BrendanSimon
источник
[hex(ord(c)) for c in s]
Борис
2

Это можно сделать следующими способами:

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

Ghansham
источник
где я могу найти будущее
тофутим
Для дальнейшего использования __future__- это стандартная библиотека, доступная в последних версиях Python 2, которую можно использовать для создания функций, обычно только в Python 3 с обратной совместимостью. В этом ответе он используется для получения print(text)функции «функция печати», которая заменяет print textсинтаксис из Python 2. См. Документы по Python .
Эрик Рид
2

Немного более общий для тех, кто не заботится о Python3 или двоеточиях:

from codecs import encode

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

print(encode(b"hello", 'hex'))  # string
Гринго Суаве
источник
0

Использование 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'
Шиплу Мокаддим
источник
Это не работает Что вы используете для импорта и почему не используете .decode()?
not2qubit
0

Просто для удобства, очень просто.

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
источник
0

для чего-то, что предлагает большую производительность, чем ''.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).

Tcll
источник