Как я пишу необработанные двоичные данные в Python?

У меня есть программа 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?
  • Когда я выписываю необработанные двоичные данные через кодек, как я кодирую/некодирую его?
10
задан Chris B. 9 April 2010 в 21:58
поделиться

3 ответа

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

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

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

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

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

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

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

    • Если вам требуется смесь 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 , если вы точно не понимаете, зачем вам это нужно.
    Я просто упоминаю ее как другую "естественную" кодировку, которая приходит на ум .

22
ответ дан 3 December 2019 в 17:57
поделиться

Отвечу на ваш первый вопрос: в 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
ответ дан 3 December 2019 в 17:57
поделиться

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

0
ответ дан 3 December 2019 в 17:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: