8

У меня есть строка юникода, полученная из webservice с использованием requests module, , которая содержит байты двоичного документа (PCL, как это бывает). Один из этих байтов имеет значение 248, и попытки base64 кодирования это приводит к следующей ошибке:base64 кодировка строк unicode в python 2.7

In [68]: base64.b64encode(response_dict['content']+'\n') 
--------------------------------------------------------------------------- 
UnicodeEncodeError      Traceback (most recent call last) 
C:\...\<ipython-input-68-8c1f1913eb52> in <module>() 
----> 1 base64.b64encode(response_dict['content']+'\n') 

C:\Python27\Lib\base64.pyc in b64encode(s, altchars) 
    51  """ 
    52  # Strip off the trailing newline 
---> 53  encoded = binascii.b2a_base64(s)[:-1] 
    54  if altchars is not None: 
    55   return _translate(encoded, {'+': altchars[0], '/': altchars[1]}) 

UnicodeEncodeError: 'ascii' codec can't encode character u'\xf8' in position 272: ordinal not in range(128) 

In [69]: response_dict['content'].encode('base64') 
--------------------------------------------------------------------------- 
UnicodeEncodeError      Traceback (most recent call last) 
C:\...\<ipython-input-69-7fd349f35f04> in <module>() 
----> 1 response_dict['content'].encode('base64') 

C:\...\base64_codec.pyc in base64_encode(input, errors) 
    22  """ 
    23  assert errors == 'strict' 
---> 24  output = base64.encodestring(input) 
    25  return (output, len(input)) 
    26 

C:\Python27\Lib\base64.pyc in encodestring(s) 
    313  for i in range(0, len(s), MAXBINSIZE): 
    314   chunk = s[i : i + MAXBINSIZE] 
--> 315   pieces.append(binascii.b2a_base64(chunk)) 
    316  return "".join(pieces) 
    317 

UnicodeEncodeError: 'ascii' codec can't encode character u'\xf8' in position 44: ordinal not in range(128) 

Я нахожу это немного удивительно, потому что 248 находится в пределах диапазона без знака байта (и может быть проведена в строка байта), но мой реальный вопрос: Каков наилучший или правильный способ кодирования этой строки?

Моя текущая Обходной это:

In [74]: byte_string = ''.join(map(compose(chr, ord), response_dict['content'])) 

In [75]: byte_string[272] 
Out[75]: '\xf8' 

Это, как представляется, правильно работать, и в результате byte_string может быть закодирована base64, но мне кажется, что там должно быть лучше. Здесь?

+1

248 может находиться в пределах диапазона беззнакового байта, но он не находится в диапазоне стандартизованного ASCII [0-127]. – Cameron

+0

@Cameron: истинный и хороший момент, но он все еще не объясняет проблему, поскольку точно такое же значение, когда оно хранится в байтовой строке, не приводит к этой ошибке. – Marcin

+0

См. Мой ответ :-). Что вы сделали, это взять кодовые точки строки 'unicode' и рассматривать их как байты. Это ... fishy в лучшем случае, так как вы не гарантируете, что кодовые точки даже находятся в диапазоне 0-255. Что еще хуже, так это то, что никто больше не будет знать, как интерпретировать байтовую строку позже, так как она находится в пользовательской неопределенной кодировке. – Cameron

ответ

2

Поскольку вы работаете с двоичными данными, я не уверен, что использовать кодировку utf-8 является хорошей идеей. Я думаю, это зависит от того, как вы собираетесь использовать кодированное base64 представление. Я думаю, что было бы лучше, если бы вы могли получить данные как строку байтов, а не строку юникода. Я никогда не использовал библиотеку запросов, но просмотр документации показывает, что это возможно. Существуют разделы, посвященные «Бинарному ответному контенту» и «Сырному ответному контенту».

+0

Спасибо! Оказывается, что кодирование как latin-1 дает ту же самую последовательность байтов, что и мой обходной путь. – Marcin

+1

@Marcin: вам нужно убедиться, что модуль запросов не предполагает, что вы работаете с текстом, применяете кодировку по умолчанию и декодируете свои двоичные данные в unicode. Если это так, у вас проблемы. Можете ли вы проверить, что контент - это то, что вы ожидаете? –

+2

Оплатив немного больше внимания на документы, выясняется, что запросы также сообщают мне кодировку, которая используется для декодирования ответа на unicode, поэтому я могу надежно всегда перекодировать с этим (и это еще раз дает то же самое байт). – Marcin

5

Я хотел бы предложить первый кодирующий его в нечто вроде UTF-8 перед кодированием base64:

In [12]: my_unicode = u'\xf8' 

In [13]: my_utf8 = my_unicode.encode('utf-8') 

In [15]: base64.b64encode(my_utf8) 
Out[15]: 'w7g=' 
+0

* кодировка UTF-8 * не имеет смысла. либо вы кодируете из UTF-8 в bytes/ascii, либо декодируете с ascii на UTF-8. это наоборот. – sebix

14

У вас есть unicode строку, которую вы хотите base64 кодирования. Проблема в том, что b64encode() работает только с байтами, а не символов. Итак, вам нужно преобразовать строку unicode (которая представляет собой последовательность абстрактных кодовых точек Unicode) в байтовую строку.

Отображение абстрактных строк Unicode в конкретную серию байтов называется кодировкой. Python поддерживает несколько кодировок; Я предлагаю широко используемый в UTF-8 кодировке:

byte_string = response_dict['content'].encode('utf-8') 

Обратите внимание, что тот, кто декодированием байт будет также необходимо знать, какая кодировка была использована, чтобы получить обратно unicode строку с помощью дополнительной decode() функции:

# Decode 
decoded = byte_string.decode('utf-8') 

Хорошая отправная точка для получения дополнительной информации о Юникоде и кодировках - это Python docs и this article от Joel Spolsky.

+0

Чтобы быть ясным: содержимое моей строки юникода - это двоичные данные. Я не могу изменить их на несколько разных байтов. Существует ли идентификационная кодировка? – Marcin

+1

@Marcin: Вы не можете иметь строку 'unicode', содержащую двоичные данные. Это противоречие в терминах! Если байты строки 'unicode' должны представлять двоичные данные (как представляется, здесь), то они не должны храниться в объекте' unicode', поскольку это вообще не Юникод! – Cameron

+0

Да, это моя проблема. – Marcin

1

Должно быть возможно получить ответ как двоичные байты и полностью пропустить шаги декодирования и кодирования. Всегда есть вероятность, что requests выберет кодировку, которая теряет некоторые данные или ошибки в поездке туда и обратно.

Эта часть документов, называемая "Binary Response Content", кажется, идеально подходит для вашей проблемы.

0

Если это двоичные данные ... зачем кодировать/декодировать вообще? Специально "base64.encodestring" часть.Ниже приведен пример того, как я кодирую изображения в base64 для непосредственного добавления в свой код python вместо дополнительных файлов. 2.7.2 btw

import base64 
iconfile = open("blah.icon","rb") 
icondata = iconfile.read() 
icondata = base64.b64encode(icondata) 
Смежные вопросы