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

Ответ 1

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

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

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

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

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

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

    Затем вы не должны отправлять его через кодек; вы должны это написать непосредственно в файл. Кодек кодирует все и может испускать действительные кодировки 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 от произвольных байтов до unicode:

    Передайте data.decode('latin1') в кодек. latin1 карты байты 0-255 для символов Юникода 0-255, который является изящным.

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

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

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

Ответ 2

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

Ответ 3

Для вашего первого вопроса: в 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