Чтобы интерпретировать последовательность байтов как текст, вы должны знать
соответствующая кодировка символов:
unicode_text = bytestring.decode(character_encoding)
Пример:
>>> b'\xc2\xb5'.decode('utf-8')
'µ'
Команда
ls
может выдавать выходные данные, которые нельзя интерпретировать как текст. Имена файлов
в Unix может быть любая последовательность байтов, кроме косой черты b'/'
и нуля
b'\0'
:
>>> open(bytes(range(0x100)).translate(None, b'\0/'), 'w').close()
Попытка декодировать такой байтовый суп с использованием кодировки utf-8 вызывает UnicodeDecodeError
.
Может быть и хуже. Декодирование может завершиться сбоем и привести к появлению моджибаке
если вы используете неправильную несовместимую кодировку:
>>> '—'.encode('utf-8').decode('cp1252')
'—'
Данные повреждены, но ваша программа не знает, что сбой
произошло.
Как правило, кодировка символов не включается в саму последовательность байтов. Вы должны передавать эту информацию по внеполосному каналу. Некоторые результаты более вероятны, чем другие, поэтому существует модуль chardet
, который может угадать кодировку символов. Один скрипт Python может использовать несколько кодировок символов в разных местах.
ls
вывод может быть преобразован в строку Python с помощью os.fsdecode()
функция, которая успешна даже для некодируемых
имена файлов (он использует
sys.getfilesystemencoding()
и surrogateescape
обработчик ошибок на
Unix):
import os
import subprocess
output = os.fsdecode(subprocess.check_output('ls'))
Чтобы получить исходные байты, вы можете использовать os.fsencode()
.
Если вы передаете параметр universal_newlines=True
, то subprocess
использует
locale.getpreferredencoding(False)
для декодирования байтов, например может быть
cp1252
в Windows.
Чтобы декодировать поток байтов на лету,
io.TextIOWrapper()
может использоваться: пример.
В разных командах могут использоваться разные кодировки символов.
вывод например dir
внутренняя команда (cmd
) может использовать cp437. Чтобы расшифровать его
вывода, вы можете явно передать кодировку (Python 3.6+):
output = subprocess.check_output('dir', shell=True, encoding='cp437')
Имена файлов могут отличаться от os.listdir()
(в котором используется Windows
Unicode API), например. '\xb6'
можно заменить на '\x14'
—Python's
Кодек cp437 отображает b'\x14'
на управляющий символ U + 0014 вместо
U + 00B6 (¶). Для поддержки имен файлов с произвольными символами Unicode см. Декодирование выходных данных PowerShell, которые могут содержать символы Unicode, отличные от ASCII, в строку Python
почему не работает
str(text_bytes)
? Мне это кажется странным.@CharlieParker Потому что
str(text_bytes)
не может указать кодировку. В зависимости от того, что находится в text_bytes,text_bytes.decode('cp1250
) `может привести к совершенно другой строке, чемtext_bytes.decode('utf-8')
.поэтому функция
str
больше не преобразуется в настоящую строку. По какой-то причине нужно явно указать кодировку, мне лень читать почему. Просто преобразуйте его вutf-8
и посмотрите, работает ли ваш код. напримерvar = var.decode('utf-8')
@CraigAnderson:
unicode_text = str(bytestring, character_encoding)
работает должным образом на Python 3. Хотяunicode_text = bytestring.decode(character_encoding)
предпочтительнее, чтобы избежать путаницы с простоstr(bytes_obj)
, который создает текстовое представление дляbytes_obj
вместо его декодирования в текст:str(b'\xb6', 'cp1252') == b'\xb6'.decode('cp1252') == '¶'
иstr(b'\xb6') == "b'\\xb6'" == repr(b'\xb6') != '¶'