Декодировать шестнадцатеричную строку в Python 3

В Python 2 преобразование шестнадцатеричной формы строки в соответствующий юникод было простым:

comments.decode("hex")

где переменная 'comments' является частью строки в файле (остальную часть строки не нужно преобразовывать, так как она представлена ​​только в ASCII.

Однако теперь в Python 3 это не работает (я полагаю, из-за переключателя байты/строка против строки/юникода. Мне кажется, что в Python 3 должен быть однострочный код, чтобы делать то же самое, а не читать всю строку как серию байтов (что я не хочу делать), а затем преобразовать каждую часть строки отдельно.Если это возможно, я хотел бы прочитать всю строку как строку Unicode (потому что остальная часть строка находится в юникоде) и преобразуйте только эту часть из шестнадцатеричного представления.


person chimeracoder    schedule 19.07.2010    source источник
comment
Я не уверен, что строки шестнадцатеричной кодировки имеют такой смысл. Если вы хотите сохранить несовместимую кодировку, я бы по крайней мере использовал базу 64, потому что она более эффективна. Конечно, это никоим образом не лишает законной силы вопрос/ответ, может быть, кто-то еще решил шестнадцатеричный.   -  person Maarten Bodewes    schedule 15.11.2018


Ответы (3)


Что-то типа:

>>> bytes.fromhex('4a4b4c').decode('utf-8')
'JKL'

Просто введите фактическую кодировку, которую вы используете.

person unbeli    schedule 19.07.2010
comment
Если декодированная строка является на самом деле utf-8, я бы рекомендовал вместо этого использовать decode('ascii'). - person Ja͢ck; 10.03.2014
comment
@Ja͢ck Вы могли бы кодировать в шестнадцатеричном формате, если бы знали, что строка Unicode несовместима с кодировкой, используемой для хранения строки. Если строка уже известна как ASCII, то в первую очередь нет необходимости кодировать ее как шестнадцатеричную строку. - person Maarten Bodewes; 15.11.2018
comment
Однако не работает для всех шестнадцатеричных строк. Например, bytes.fromhex('82').decode('utf-8') повышает UnicodeDecodeError. Использование формата 'ascii' не решает проблему, так как это не сработает для байтов со значениями ›127. - person HackerBoss; 12.09.2019
comment
Это потому, что 0x82 действительно не является допустимой последовательностью UTF-8. Ваш комментарий тривиально верен в том, что шестнадцатеричные строки, которые не являются допустимыми UTF-8, не могут быть декодированы, но это было бы верно и для любого другого представления этих последовательностей. - person tripleee; 18.12.2019
comment
@MaartenBodewes Есть причины для кодирования в шестнадцатеричном формате, даже если это уже ASCII. Например, если вы хотите использовать его в качестве имени файла, вы можете избегать использования таких символов, как '/' или '\', и шестнадцатеричная кодировка исправит это. - person Buge; 23.02.2020
comment
@Buge Это хороший момент, хотя base64url может иметь больше смысла для этого конкретного случая использования. - person Maarten Bodewes; 23.02.2020

Ответы от @unbeli и @Niklas хороши, но ответ @unbeli работает не для всех шестнадцатеричных строк и желательно делать декодирование без импорта лишней библиотеки (кодеков). Следующее должно работать (но будет не очень эффективно для больших строк):

>>> result = bytes.fromhex((lambda s: ("%s%s00" * (len(s)//2)) % tuple(s))('4a82fdfeff00')).decode('utf-16-le')
>>> result == '\x4a\x82\xfd\xfe\xff\x00'
True

По сути, он работает с недопустимыми байтами utf-8, заполняя нулями и декодируя как utf-16.

person HackerBoss    schedule 12.09.2019
comment
Вы неправильно понимаете, как работает UTF-8. Но если вы вводите UTF-16 (или, точнее, чистое 16-битное подмножество UCS-2), это полезно. - person tripleee; 18.12.2019

person    schedule
comment
Этот метод универсален и отлично работает как для py2, так и для py3. Спасибо! - person MarSoft; 02.10.2018