Кодировать и декодировать строку байтов Python

Я пытаюсь преобразовать входящую строку байтов, содержащую символы, отличные от ascii, в действительную строку utf-8, которую я могу сбросить как json.

b = '\x80'
u8 = b.encode('utf-8')
j = json.dumps(u8)

Я ожидал, что j будет '\xc2\x80', но вместо этого я получаю:

UnicodeDecodeError: 'ascii' codec can't decode byte 0x80 in position 0: ordinal not in range(128)

В моей ситуации «b» исходит из mysql через буферы протокола Google и заполняется некоторыми данными blob.

Есть идеи?

РЕДАКТИРОВАТЬ: у меня есть кадры Ethernet, которые хранятся в таблице mysql в виде большого двоичного объекта (пожалуйста, все, оставайтесь в теме и не обсуждайте, почему в таблице есть пакеты). Сопоставление таблиц - utf-8, а уровень db (sqlalchemy, non-orm) захватывает данные и создает структуры (буферы протокола Google), которые хранят большой двоичный объект как python 'str'. В некоторых случаях я использую буферы протокола напрямую без каких-либо проблем. В других случаях мне нужно предоставить те же данные через json. Что я заметил, так это то, что когда json.dumps() делает свое дело, «\ x80» может быть заменен недопустимым символом юникода (\ ufffd iirc)


person kung-foo    schedule 07.03.2012    source источник
comment
Вам нужно предоставить фрагмент кода, который показывает, что использует буферы протокола напрямую, без каких-либо проблем. Вам нужно показать фрагмент кода, что вы делаете с буфером протокола, чтобы заставить json.dumps производить �. Вам нужно точно сказать, что должен сделать потребитель этого JSON-пакета, чтобы восстановить исходный пакет.   -  person John Machin    schedule 08.03.2012


Ответы (3)


Вам необходимо изучить документацию по программному API, который вы используете. BLOB — это аббревиатура: BINARY Большой объект.

Если ваши данные на самом деле двоичные, идея декодирования их в Unicode, конечно, абсурдна.

Если это на самом деле текст, вам нужно знать, какую кодировку использовать для его декодирования в Unicode.

Затем вы используете json.dumps(a_Python_object) ... если вы сами кодируете его в UTF-8, json снова декодирует его:

>>> import json
>>> json.dumps(u"\u0100\u0404")
'"\\u0100\\u0404"'
>>> json.dumps(u"\u0100\u0404".encode('utf8'))
'"\\u0100\\u0404"'
>>>

ОБНОВЛЕНИЕ о latin1:

u'\x80' — это бесполезный бессмысленный управляющий символ C1 — крайне маловероятно, что кодировка будет Latin-1. Latin-1 — это «ловушка и заблуждение» — все 8-битные байты декодируются в Unicode без возникновения исключения. Не путайте "работает" и "не вызывает исключения".

person John Machin    schedule 07.03.2012
comment
интересно. я думаю, я могу сделать это проще: print json.dumps('\x80'.decode('latin1')) - person kung-foo; 07.03.2012
comment
@kung-foo: у вас нет доказательств того, что latin1 является правильной кодировкой. - person John Machin; 07.03.2012
comment
Тогда каков метод кодирования строки байтов в utf-8? - person kung-foo; 07.03.2012
comment
При чем здесь BLOB? - person Marcin; 07.03.2012
comment
@kung-foo: это метод a_string_of_bytes.decode('some_encoding').encode('utf8') ... однако вам не нужно кодировать в utf8, чтобы иметь возможность использовать json.dumps; вам ДЕЙСТВИТЕЛЬНО нужно установить, являются ли ваши данные текстом, и если да, то что такое some_encoding, а latin1 маловероятно ... ммм, другими словами: просто медленно и внимательно перечитайте мой ответ. - person John Machin; 07.03.2012
comment
@Marcin: цитирование OP: заполнено некоторыми данными blob - person John Machin; 07.03.2012
comment
Итак, каково ваше решение для получения этих двоичных данных в строке JSON, если сначала не вставить их в объект Unicode? - person Marcin; 07.03.2012
comment
@Marcin: OP должен прочитать документы для API, который он использует для ввода, и документы для всего, что будет использовать строку JSON. Делать предложения без полного описания проблемы НЕ полезно. - person John Machin; 07.03.2012
comment
@JohnMachin Похоже, вы отказываетесь учитывать тот факт, что кодировщик json будет работать только с Unicode. - person Marcin; 07.03.2012
comment
Не мог бы анонимный даунвотер объяснить, какая часть моего ответа считается неверной? - person John Machin; 07.03.2012
comment
@Marcin: отказываться участвовать: чепуха. Например. Потребителю может потребоваться строка в кодировке base-64, и в этом случае нужно будет закодировать исходные данные в base-64, а затем использовать json.dumps(b64_encoded_data.decode('ascii')) — предположения бессмысленны. - person John Machin; 07.03.2012
comment
@JohnMachin: у меня есть кадры Ethernet, которые хранятся в таблице mysql в виде большого двоичного объекта (пожалуйста, все, оставайтесь в теме и не обсуждайте, почему в таблице есть пакеты). Сопоставление таблиц - utf-8, а уровень db (sqlalchemy, non-orm) захватывает данные и создает структуры (буферы протокола Google), которые хранят большой двоичный объект как python 'str'. В некоторых случаях я использую буферы протокола напрямую без каких-либо проблем. В других случаях мне нужно предоставить те же данные через json. Я заметил, что когда json.dumps() делает свое дело, '\ x80' может быть заменен недопустимым символом юникода (\ ufffd iirc). - person kung-foo; 07.03.2012

Используйте b.decode('name of source encoding'), чтобы получить версию Unicode. Меня это удивило, когда я узнал об этом. например:

In [123]: 'foo'.decode('latin-1')
Out[123]: u'foo'
person Marcin    schedule 07.03.2012
comment
Помните: decode переходит от байтов к юникоду. encode переходит из юникода в байты. - person Daniel Roseman; 07.03.2012
comment
@DanielRoseman Да, именно поэтому это ответ на вопрос. - person Marcin; 07.03.2012
comment
Конечно, я не спорил, просто дал дополнительное объяснение ОП. - person Daniel Roseman; 07.03.2012
comment
Я придумал то же самое, но это казалось таким неэффективным. Я удивлен, что нет способа напрямую закодировать utf-8 строку байтов. - person kung-foo; 07.03.2012
comment
@kung-foo Что ты имеешь в виду? В чем это не прямо? - person Marcin; 07.03.2012
comment
-1 (а) Вы не можете вывести кодировку из одного байта! (b) u'\x80' — бесполезный бессмысленный управляющий символ C1 — кодировка крайне маловероятна как Latin-1. (e) кодирование в UTF-8 бессмысленно - см. мой ответ. - person John Machin; 07.03.2012
comment
Отсюда мой первоначальный вопрос. У меня есть строка «байт». Нет кодировки. Но кодирование в utf-8 дает мне гибкость для передачи данных. - person kung-foo; 07.03.2012
comment
@JohnMachin Где я могу предположить, что можно вывести кодировку из одного байта или предположить, что кодировка является latin-1? В любом случае, учитывая, что latin-1 соответствует UTF-8 для кодовых точек до 255, это довольно безопасный выбор, если нет реальной кодировки. - person Marcin; 07.03.2012
comment
@kung-foo Вы серьезно запутались - строки юникода предназначены для представления строк символов (абстрактных соответствий кодовых точек юникода); байтовые строки предназначены для представления байтов. Если это не текстовые данные, сохраните их как строку байтов. Если это данные, которые уже закодированы в utf-8, декодируйте их как utf-8. - person Marcin; 07.03.2012
comment
@kung-foo: Хотя, я думаю, в этом случае, если вы транспортируете в формате JSON, у вас нет другого выбора, кроме как вставить это в юникод. - person Marcin; 07.03.2012
comment
@Marcin: Marcin: в текущем контексте latin1 - нелепый выбор для примера того, как декодировать. latin-1 соответствует UTF-8 для кодовых точек до 255, не имеет смысла — unichr(255).encode('utf8') дает \xc3\xbf, а unichr(255).encode('latin1') дает '\xff'. Если нет реального кодирования, то данные не могут быть осмысленно декодированы. - person John Machin; 07.03.2012

Я думаю, что вы пытаетесь декодировать строковый объект некоторой кодировки. Вы знаете, что это за кодировка? Чтобы получить объект unicode.

unicode_b = b.decode('some_encoding')

а затем перекодировать объект unicode, используя кодировку utf_8, обратно в строковый объект.

b = unicode_b.encode('utf_8')

Используя объект unicode в качестве переводчика, не зная исходной кодировки строки, я не могу знать наверняка, но есть вероятность, что преобразование пойдет не так, как ожидалось. Объект unicode не предназначен для преобразования строк одной кодировки в другую. Я бы работал с объектом unicode, предполагая, что вы знаете, что такое кодировка, если вы не знаете, что такое кодировка, то действительно нет способа узнать это без проб и ошибок, а затем преобразовать обратно в закодированную строку, когда вы хотите вернуть строковый объект.

person snarkyname77    schedule 07.03.2012