Как правильно преобразовать байты в шестнадцатеричную строку в Python 3?

236

Как правильно преобразовать байты в шестнадцатеричную строку в Python 3?

Я вижу утверждения bytes.hexметода, bytes.decodeкодеков и пробовал другие возможные функции наименьшего удивления безрезультатно. Я просто хочу, чтобы мои байты были шестнадцатеричными!

Мэтт Джойнер
источник
"безрезультатно"? Какие конкретные проблемы или ошибки вы получаете? Пожалуйста, покажите код и ошибки.
S.Lott
Возможный дубликат stackoverflow.com/questions/2340319/python-3-1-1-string-to-hex
Mu Mind

Ответы:

410

Начиная с Python 3.5 это, наконец, больше не неудобно:

>>> b'\xde\xad\xbe\xef'.hex()
'deadbeef'

и наоборот:

>>> bytes.fromhex('deadbeef')
b'\xde\xad\xbe\xef'

работает также с изменяемым bytearrayтипом.

Ссылка: https://docs.python.org/3/library/stdtypes.html#bytes.hex

Феликс Вейс
источник
5
bytes.fromhex()также доступен на Python 3.0+ (не только 3.5+). bytes.hex()только на Python 3.5+.
Феникс
95

Используйте binasciiмодуль:

>>> import binascii
>>> binascii.hexlify('foo'.encode('utf8'))
b'666f6f'
>>> binascii.unhexlify(_).decode('utf8')
'foo'

Посмотрите этот ответ: строка Python 3.1.1 в hex

Му Разум
источник
8
Это хорошо. Поразительно, что вы можете конвертировать шестнадцатеричные числа в байты с помощью bytes.fromhex (hex_str), но вы не можете преобразовать байты в шестнадцатеричные с помощью bytes.tohex () - что в этом рационального?
nagylzs
1
Я предполагаю, что отношения между байтами и шестнадцатеричными не являются свойством либо (что не отвечает, почему fromhex существует). Похоже, это был не просто недосмотр, а спор: bugs.python.org/issue3532#msg70950 . Q: Было бы больно иметь метод tohex объекта bytes для выполнения этой задачи? A: ИМО, да, было бы. Это усложняет код и отвлекает внимание от правильного подхода к преобразованию данных (а именно функций, а не методов).
Му разум
3
Это действительно отвечает на вопрос? Он не возвращает гекс, strно bytes. Я знаю, что ОП кажется довольным ответом, но не будет лучше расширить этот ответ, включив в него .decode("ascii")также преобразование его в «строку»
RubenLaguna
3
Я думал, что многие люди садятся на этот вопрос / ответ в поисках способа распечатать bytes. Если вы print(b'666f6f')получите bв распечатке. Если ты, .decode("ascii")то нет. Просто подумав о том, как те, кто на самом деле имеет bytes(истинный двоичный файл с элементами> 128, а не строку ascii), хотят распечатать его.
РубенЛагуна
5
@nagylzs: есть .hex()метод в Python 3.5+
JFS
43

Python имеет стандартные кодеки от байтов к байту, которые выполняют удобные преобразования, такие как кавычка-печать (вписывается в 7-битную ascii), base64 (вписывается в алфавитно-цифровые символы), экранирование в шестнадцатеричном формате, сжатие gzip и bz2. В Python 2 вы можете сделать:

b'foo'.encode('hex')

В Python 3 str.encode/ bytes.decodeстрого для преобразования байтов <-> str. Вместо этого вы можете сделать это, что работает в Python 2 и Python 3 ( s / encode / decode / g для обратного):

import codecs
codecs.getencoder('hex')(b'foo')[0]

Начиная с Python 3.4, есть менее неудобная опция:

codecs.encode(b'foo', 'hex')

Эти кодеки misc также доступны внутри их собственных модулей (base64, zlib, bz2, uu, quopri, binascii); API менее согласован, но для кодеков сжатия он предлагает больше контроля.

Габриель
источник
1
используя python 3.3:LookupError: unknown encoding: hex
Янус Троелсен
@JanusTroelsen: попробуйте 'hex_codec' . Или просто использовать binascii.hexlify(b'foo')напрямую
JFS
7
import codecs
codecs.getencoder('hex_codec')(b'foo')[0]

работает в Python 3.3 (поэтому "hex_codec" вместо "hex").

Ричард Кисс
источник
Возможно, интересно, что в Python 3.4 «hex» или «hex_codec» работает нормально.
Стивен Полгер,
6

Метод binascii.hexlify()преобразуется bytesв bytesпредставляющую шестнадцатеричную строку ascii. Это означает, что каждый байт на входе будет преобразован в два символа ascii. Если вы хотите, чтобы правда str, то вы можете.decode("ascii") результат.

Я включил фрагмент, который иллюстрирует это.

import binascii

with open("addressbook.bin", "rb") as f: # or any binary file like '/bin/ls'
    in_bytes = f.read()
    print(in_bytes) # b'\n\x16\n\x04'
    hex_bytes = binascii.hexlify(in_bytes) 
    print(hex_bytes) # b'0a160a04' which is twice as long as in_bytes
    hex_str = hex_bytes.decode("ascii")
    print(hex_str) # 0a160a04

из шестнадцатеричной строки , "0a160a04"чтобы можно вернуться к bytesс , binascii.unhexlify("0a160a04")что дает обратноb'\n\x16\n\x04'

RubenLaguna
источник
3

Хорошо, следующий ответ немного выходит за рамки, если вы заботитесь только о Python 3, но этот вопрос является первым хитом Google, даже если вы не указали версию Python, поэтому вот способ, который работает как на Python 2, так и на Python 3 ,

Я также интерпретирую вопрос о преобразовании байтов в str тип: то есть bytes-y на Python 2 и Unicode-y на Python 3.

Учитывая это, лучший подход, который я знаю, это:

import six

bytes_to_hex_str = lambda b: ' '.join('%02x' % i for i in six.iterbytes(b))

Следующее утверждение будет верно для Python 2 или Python 3, при условии, что вы не активировали unicode_literalsбудущее в Python 2:

assert bytes_to_hex_str(b'jkl') == '6a 6b 6c'

(Или вы можете использовать, ''.join()чтобы пропустить пробел между байтами и т. Д.)

Питер
источник
3

можно использовать спецификатор %x02формата, который форматирует и выводит шестнадцатеричное значение. Например:

>>> foo = b"tC\xfc}\x05i\x8d\x86\x05\xa5\xb4\xd3]Vd\x9cZ\x92~'6"
>>> res = ""
>>> for b in foo:
...     res += "%02x" % b
... 
>>> print(res)
7443fc7d05698d8605a5b4d35d56649c5a927e2736
Arg0s
источник
По моему мнению, это лучший ответ, потому что он работает с каждой версией Python и не требует импорта. Тем не менее, я бы лучше отображал строки в шестнадцатеричном формате в верхнем регистреres.upper()
Bruno L.
3

Новое в Python 3.8, вы можете передать аргумент разделителя hexфункции, как в этом примере

>>> value = b'\xf0\xf1\xf2'
>>> value.hex('-')
'f0-f1-f2'
>>> value.hex('_', 2)
'f0_f1f2'
>>> b'UUDDLRLRAB'.hex(' ', -4)
'55554444 4c524c52 4142'

https://docs.python.org/3/library/stdtypes.html#bytes.hex

Питер Митрано
источник
0

Если вы хотите преобразовать b '\ x61' в 97 или '0x61', вы можете попробовать это:

[python3.5]
>>>from struct import *
>>>temp=unpack('B',b'\x61')[0] ## convert bytes to unsigned int
97
>>>hex(temp) ##convert int to string which is hexadecimal expression
'0x61'

Ссылка: https://docs.python.org/3.5/library/struct.html

Хао Ли
источник
Как-то помогает мне с esp32
Tejas Tank