У меня есть программа Python, которая хранит и пишет данные в файл. Данные являются необработанными двоичными данными, хранившими внутренне как str
. Я выписываю его через utf-8 кодек. Однако я добираюсь UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 25: character maps to <undefined>
в cp1252.py
файл.
Это смотрит на меня как Python, пытается интерпретировать данные с помощью кодовой страницы по умолчанию. Но это не имеет кодовой страницы по умолчанию. Вот почему я использую str
, нет unicode
.
Я предполагаю, что мои вопросы:
ПРИМЕЧАНИЕ: это было написано для Python 2.x. Не уверен, применимо ли это к 3.x.
Вы правильно используете str
для сырых двоичных данных в памяти.
[Если вы используете Python 2.6+, лучше использовать байтов
, которые в 2.6+ - это просто псевдоним для str
, но он лучше выражает ваше намерение и поможет, если однажды вы перенесете код на Python 3.]
Как отмечают другие, запись двоичных данных через кодек - это странно . Кодек записи принимает юникод , а выводит байты в файл. Вы пытаетесь сделать это в обратном порядке, поэтому мы не понимаем ваших намерений ...
[И ваш диагноз ошибки выглядит правильным: поскольку кодек ожидает Unicode, Python декодирует вашу str в unicode с кодировкой системы по умолчанию, что мешает.]
Что вы хотите видеть в выходном файле?
Если файл должен содержать двоичные данные как есть :
Тогда вы не должны отправлять его через кодек; вы должны записать его прямо в файл. Кодек кодирует все и может только выдавать допустимые кодировки Unicode (в вашем случае действительный UTF-8). Нет данных, которые вы можете дать ему, чтобы сделать это испускать произвольные байтовые последовательности!
some_data
{{1} } с some_text.encode ('utf8')
... Обратите внимание, однако, что смешивание UTF-8 с необработанными произвольными данными - это очень плохой дизайн, потому что такие файлы очень неудобны для иметь дело с ! Инструменты, которые понимают Unicode, будут подавлять двоичные данные , оставляя вам неудобный способ даже просматривать (не говоря уже о изменении) файла.
Если вы хотите дружественное представление произвольных байтов в юникоде :
Передайте data.encode ('base64')
в кодек.Base64 создает только
чистый ascii (буквы, цифры и небольшую пунктуацию), поэтому его
можно четко встроить во что угодно, он явно выглядит для людей как
двоичные данные, и это достаточно компактный (чуть более 33%
накладных расходов).
P.S. вы можете заметить, что data.encode ('base64')
выглядит странно.
.encode ()
должен принимать Unicode, но я даю ему строку
?! В Python есть несколько псевдокодеков, которые преобразуют str-> str
, например base64 и zlib.
.encode ()
всегда возвращает строку, но вы передадите ее в кодек
, ожидающий Unicode ?! В этом случае он будет содержать только чистый
ascii, так что это не имеет значения. Вы можете явно написать
data.encode ('base64'). Encode ('utf8')
, если вам станет легче
.
Если вам нужно отображение 1: 1 из произвольных байтов в Unicode :
Передайте data.decode ('latin1')
в кодек. latin1
отображает
байтов 0–255 в символы юникода 0–255, что довольно элегантно.
Кодек, конечно же, закодирует ваши символы - 128–255 закодированы как 2 или 3 байта в UTF-8 (удивительно, что средние накладные расходы составляют 50%, более чем base64!). Это полностью убивает "элегантность" отображения 1: 1.
Обратите внимание, что символы юникода 0–255 включают неприятные невидимые / управляющие символы (новая строка, перевод страницы, мягкий дефис и т. Д.) , что делает ваши двоичные данные раздражающими при просмотре в текстовых редакторах.
Учитывая эти недостатки, я не рекомендую latin1 , если
вы точно не понимаете, зачем вам это нужно.
Я просто упоминаю ее как другую "естественную" кодировку, которая приходит на ум
.
Отвечу на ваш первый вопрос: в Python обычные строки (т. Е. Не строки Unicode) являются двоичными данными. Если вы хотите записать строки Unicode и двоичные данные, превратите строки Unicode в двоичные данные и соедините их вместе:
# encode the unicode string as a string
bytes = unicodeString.encode('utf-8')
# add it to the other string
raw_data += bytes
# write it all to a file
yourFile.write(raw_data)
Для вашего второго вопроса: вы write ()
необработанные данные; затем, когда вы его читаете, вы делаете это примерно так:
import codecs
yourFile = codecs.open( "yourFileName", "r", "utf-8" )
# and now just use yourFile.read() to read it
Обычно не следует использовать кодеки с str
, кроме как для преобразования их в unicode
s. Возможно, вам стоит рассмотреть возможность использования кодека latin-1
, если вы думаете, что вам нужны «сырые» данные в ваших юникодах.