base64 кодирование строк Unicode в Python 2.7 - PullRequest
9 голосов
/ 05 марта 2012

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

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, но, похоже, должен быть лучший способ. Есть ли?

Ответы [ 5 ]

18 голосов
/ 05 марта 2012

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

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

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

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

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

Хорошей отправной точкой для получения дополнительной информации о Unicode и кодировках являются Документы Python и эта статья Джоэля Спольски.

5 голосов
/ 05 марта 2012

Я бы предложил сначала закодировать его в 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='
3 голосов
/ 05 марта 2012

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

1 голос
/ 06 марта 2012

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

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

0 голосов
/ 06 марта 2012

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

import base64
iconfile = open("blah.icon","rb")
icondata = iconfile.read()
icondata = base64.b64encode(icondata)
...