Как мне написать сырые двоичные данные в Python? - PullRequest
10 голосов
/ 10 апреля 2010

У меня есть программа на 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?
  • Когда я записываю необработанные двоичные данные через кодек, как мне их кодировать / декодировать?

Ответы [ 3 ]

22 голосов
/ 11 апреля 2010

ПРИМЕЧАНИЕ: это было написано для Python 2.x. Не уверен, если применимо к 3.x.

Вы правильно используете str для необработанных двоичных данных в памяти.
[Если вы используете Python 2.6+, даже лучше использовать bytes, который в 2.6+ является просто псевдонимом str, но лучше выражает ваше намерение и поможет, если однажды вы перенесете код на Python 3. ]

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

[И ваш диагноз ошибки выглядит правильно: так как кодек ожидает Unicode, Python декодирует ваш str в Unicode с кодировкой системы по умолчанию, которая задыхается.]

Что вы хотите видеть в выходном файле?

  • Если файл должен содержать двоичные данные как есть :

    Тогда вы не должны отправлять его через кодек; ты должен написать это прямо в файл. Кодек кодирует все и может только Выдать действительные кодировки Unicode (в вашем случае, действительный UTF-8). Нет данных, которые вы можете дать, чтобы они излучали произвольно последовательности байтов!

    • Если вам требуется смесь UTF-8 и необработанных двоичных данных, вы должен открыть файл напрямую, и перемешать записи some_data с 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() всегда возвращает str, но вы передадите его в кодек ожидая Unicode ?! В этом случае он будет содержать только чистый ASCII, так что это не имеет значения. Вы можете написать явно data.encode('base64').encode('utf8') если это заставляет тебя чувствовать лучше.

  • Если вам необходимо преобразование 1: 1 из произвольных байтов в юникод :

    Передача data.decode('latin1') в кодек. latin1 карт байты 0-255 для символов Unicode 0-255, что довольно элегантно.

    Кодек, конечно, будет кодировать ваши символы - 128-255 закодированы как 2 или 3 байта в UTF-8 (на удивление, средний накладные расходы составляют 50%, больше, чем base64!). Это довольно убивает «элегантность» наличия 1: 1.

    Обратите внимание, что символы Юникода 0-255 включают в себя противный невидимые / управляющие символы (перевод строки, подача, мягкий дефис и т. д.) что делает ваши двоичные данные раздражающими для просмотра в текстовых редакторах.

    Учитывая эти недостатки, Я не рекомендую latin1 , если только вы точно понимаете, почему вы этого хотите.
    Я просто упоминаю это как другое "естественное" кодирование, которое возникает на ум.

0 голосов
/ 10 апреля 2010

По первому вопросу: в 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
0 голосов
/ 10 апреля 2010

Обычно вы не должны использовать кодеки с str, кроме как для превращения их в unicode с. Возможно, вам стоит подумать об использовании кодека latin-1, если вы считаете, что вам нужны «необработанные» данные в ваших юникодах.

...