UnicodeEncodeError: кодек 'charmap' не может кодировать сопоставления символов в , функция печати
Я пишу программу на Python (Python 3.3) для отправки некоторых данных на веб-страницу с использованием метода POST . В основном для процесса отладки я получаю результат страницы и отображаю его на экране с помощью функции print()
.
Код выглядит следующим образом:
conn.request("POST", resource, params, headers)
response = conn.getresponse()
print(response.status, response.reason)
data = response.read()
print(data.decode('utf-8'));
HTTPResponse
.read()
метод возвращает bytes
элемент, кодирующий страницу (которая является хорошо отформатированным документом UTF-8) Все казалось нормальным, пока я не перестал использовать IDLE GUI для Windows и вместо этого использовал консоль Windows. Возвращаемая страница содержит символ U + 2014 (em-тире), который функция печати хорошо переводит в графическом интерфейсе Windows (я полагаю, кодовая страница 1252), но не в консоли Windows (кодовая страница 850). Учитывая strict
поведение по умолчанию, я получаю следующую ошибку:
UnicodeEncodeError: 'charmap' codec can't encode character '\u2014' in position 10248: character maps to <undefined>
Я мог бы исправить это, используя этот довольно уродливый код:
print(data.decode('utf-8').encode('cp850','replace').decode('cp850'))
Теперь он заменяет недопустимый символ "—" на ?
. Не идеальный вариант (дефис должен быть лучшей заменой), но достаточно хорош для моих целей.
Есть несколько вещей, которые мне не нравятся в моем решении.
- Код уродлив со всем этим декодированием, кодировкой и еще раз декодированием.
- Это решает проблему только для этого случая. Если я перенесу программу для системы, использующей какую-либо другую кодировку (latin-1, cp437, обратно в cp1252 и т.д.), Она должна распознать целевую кодировку. Это не так. (например, при повторном использовании графического интерфейса IDLE также теряется emdash, чего раньше не было)
- Было бы лучше, если бы emdash переводился в дефис вместо запроса bang.
Проблема не в emdash (я могу придумать несколько способов решить именно эту проблему), но мне нужно написать надежный код. Я загружаю на страницу данные из базы данных, и эти данные могут возвращаться. Я могу предвидеть множество других конфликтующих случаев: 'Á' U + 00c1 (что возможно в моей базе данных) может переводиться в CP-850 (кодировка консоли DOS / Windows для западноевропейских языков), но не в CP-437 (кодировка американского английского, которая используется по умолчанию во многих установках Windows).
Итак, вопрос:
Есть ли более приятное решение, которое делает мой код независимым от кодировки выходного интерфейса?
Переведено автоматически
Ответ 1
Я вижу три решения этой проблемы:
Измените кодировку вывода, чтобы она всегда выводила UTF-8 . Смотрите, Например, Установка правильной кодировки при конвейерном стандартном выводе в Python, но я не смог заставить этот пример работать.
Следующий пример кода позволяет выводить информацию о вашей целевой кодировке.
# -*- coding: utf-8 -*-
import sys
print sys.stdout.encoding
print u"Stöcker".encode(sys.stdout.encoding, errors='replace')
print u"Стоескер".encode(sys.stdout.encoding, errors='replace')В этом примере любой непечатаемый символ в моем имени правильно заменяется вопросительным знаком.
Если вы создадите пользовательскую функцию печати , например, called
myprint
, используя эти механизмы для правильного кодирования выходных данных, вы можете просто заменить print наmyprint
везде, где это необходимо, не делая весь код уродливым.Сбросьте кодировку вывода глобально в начале работы программного обеспечения:
На странице http://www.macfreek.nl/memory/Encoding_of_Python_stdout есть хорошее краткое описание того, что нужно сделать, чтобы изменить кодировку вывода. Особенно интересен раздел "StreamWriter-оболочка вокруг стандартного вывода". По сути, в нем говорится об изменении функции кодирования ввода-вывода следующим образом:
В Python 2:
if sys.stdout.encoding != 'cp850':
sys.stdout = codecs.getwriter('cp850')(sys.stdout, 'strict')
if sys.stderr.encoding != 'cp850':
sys.stderr = codecs.getwriter('cp850')(sys.stderr, 'strict')В Python 3:
if sys.stdout.encoding != 'cp850':
sys.stdout = codecs.getwriter('cp850')(sys.stdout.buffer, 'strict')
if sys.stderr.encoding != 'cp850':
sys.stderr = codecs.getwriter('cp850')(sys.stderr.buffer, 'strict')Если используется при выводе HTML CGI, вы можете заменить 'strict' на 'xmlcharrefreplace', чтобы получить теги в HTML-кодировке для непечатаемых символов.
Не стесняйтесь изменять подходы, устанавливая разные кодировки.... Обратите внимание, что по-прежнему не работает вывод неопределенных данных. Таким образом, любые данные, входные данные, тексты должны быть корректно преобразованы в unicode:
# -*- coding: utf-8 -*-
import sys
import codecs
sys.stdout = codecs.getwriter("iso-8859-1")(sys.stdout, 'xmlcharrefreplace')
print u"Stöcker" # works
print "Stöcker".decode("utf-8") # works
print "Stöcker" # fails
Ответ 2
Основываясь на ответе Дирка Стекера , вот аккуратная функция-оболочка для функции print в Python 3. Используйте ее так же, как вы использовали бы print .
В качестве дополнительного бонуса, по сравнению с другими ответами, ваш текст будет напечатан не как bytearray ('b"content"') , а как обычные строки ('content'), из-за последнего шага декодирования.
def uprint(*objects, sep=' ', end='\n', file=sys.stdout):
enc = file.encoding
if enc == 'UTF-8':
print(*objects, sep=sep, end=end, file=file)
else:
f = lambda obj: str(obj).encode(enc, errors='backslashreplace').decode(enc)
print(*map(f, objects), sep=sep, end=end, file=file)
uprint('foo')
uprint(u'Antonín Dvořák')
uprint('foo', 'bar', u'Antonín Dvořák')
Ответ 3
Я углубился в это и нашел, что лучшие решения здесь.
http://blog.notdot.net/2010/07/Getting-unicode-right-in-Python
В моем случае я решил "UnicodeEncodeError: кодек 'charmap' не может кодировать символы"
исходный код:
print("Process lines, file_name command_line %s\n"% command_line))
Новый код:
print("Process lines, file_name command_line %s\n"% command_line.encode('utf-8'))
Ответ 4
Для целей отладки вы могли бы использовать print(repr(data))
.
Для отображения текста всегда выводите Юникод. Не вводите жестко кодировку символов вашей среды, такую как Cp850, внутри вашего скрипта. Чтобы расшифровать HTTP-ответ, см. Хороший способ получить кодировку HTTP-ответа в Python.
Чтобы распечатать Unicode в консоли Windows, вы могли бы использовать win-unicode-console
package.