UnicodeEncodeError: кодек "charmap" не может кодировать - карты символов для <undefined>, функция печати
Я пишу программу 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-dash), который функция печати хорошо переносит в графическом интерфейсе 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, back to cp1252 и т.д.), Она должна распознать целевую кодировку. Это не. (например, при повторном использовании IDLE GUI, emdash также теряется, чего раньше не было)
- Было бы лучше, если бы emdash переводили в дефис вместо опроса.
Проблема не в emdash (я могу придумать несколько способов решить эту проблему), но мне нужно написать надежный код. Я загружаю страницу данными из базы данных и данные могут возвращаться. Я могу предвидеть многие другие конфликтующие случаи: "Á" U + 00c1 (что возможно в моей базе данных) может перевести на CP-850 (DOS/Windows Console encodign для западноевропейских языков), но не в CP-437 (кодировка для США Английский, который по умолчанию используется во многих установках Windows).
Итак, вопрос:
Есть ли более приятное решение, которое делает мой код агностиком из кодирования выходного интерфейса?
Ответы
Ответ 1
Я вижу три решения:
-
Измените выходную кодировку, чтобы она всегда выводила UTF-8. См. Установка правильной кодировки при отправке stdout в 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')
Этот пример правильно заменяет любой непечатаемый символ в моем имени вопросительным знаком.
Если вы создаете пользовательскую функцию печати, например. называемый myprint
, используя эти механизмы для правильного кодирования вывода, вы можете просто заменить печать myprint
необходимым, не делая весь код выглядящим уродливым.
-
Reset кодирование вывода во всем мире в начале программного обеспечения:
На странице http://www.macfreek.nl/memory/Encoding_of_Python_stdout есть хорошее резюме, что нужно сделать, чтобы изменить кодировку вывода. Особенно интересен раздел "Обтекатель StreamWriter вокруг Stdout". По сути, он говорит об изменении функции кодирования ввода-вывода следующим образом:
В 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')
Если в CGI выводится HTML-код, вы можете заменить "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
Основываясь на ответе Дирка Штелькера, здесь используется аккуратная функция обертки для функции печати Python 3. Используйте его так же, как вы использовали бы печать.
В качестве дополнительного бонуса, по сравнению с другими ответами, это не будет печатать ваш текст как bytearray ('b "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
Для целей отладки вы можете использовать print(repr(data))
.
Чтобы отобразить текст, всегда печатайте Юникод. Не перекодируйте кодировку символов вашей среды, например cp850
внутри script. Чтобы декодировать ответ HTTP, см. Хороший способ получить кодировку/кодировку ответа HTTP в Python.
Чтобы печатать Unicode в консоли Windows, вы можете использовать win-unicode-console
пакет.
Ответ 4
Я углубился в это и нашел лучшие решения здесь.
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'))
Ответ 5
Если вы используете командную строку Windows для печати данных, вы должны использовать
chcp 65001
Это сработало для меня!
Ответ 6
Если вы используете Python 3.6 (возможно, 3.5 или новее), это больше не дает мне этой ошибки. У меня была аналогичная проблема, потому что я использовал v3.4, но после того, как я удалил и переустановил, он исчез.