Вопрос-Ответ

UnicodeEncodeError: 'charmap' codec can't encode - character maps to , print function [duplicate]

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'))

Теперь он заменяет недопустимый символ "—" на ? . Не идеальный вариант (дефис должен быть лучшей заменой), но достаточно хорош для моих целей.

Есть несколько вещей, которые мне не нравятся в моем решении.


  1. Код уродлив со всем этим декодированием, кодировкой и еще раз декодированием.

  2. Это решает проблему только для этого случая. Если я перенесу программу для системы, использующей какую-либо другую кодировку (latin-1, cp437, обратно в cp1252 и т.д.), Она должна распознать целевую кодировку. Это не так. (например, при повторном использовании графического интерфейса IDLE также теряется emdash, чего раньше не было)

  3. Было бы лучше, если бы emdash переводился в дефис вместо запроса bang.

Проблема не в emdash (я могу придумать несколько способов решить именно эту проблему), но мне нужно написать надежный код. Я загружаю на страницу данные из базы данных, и эти данные могут возвращаться. Я могу предвидеть множество других конфликтующих случаев: 'Á' U + 00c1 (что возможно в моей базе данных) может переводиться в CP-850 (кодировка консоли DOS / Windows для западноевропейских языков), но не в CP-437 (кодировка американского английского, которая используется по умолчанию во многих установках Windows).

Итак, вопрос:

Есть ли более приятное решение, которое делает мой код независимым от кодировки выходного интерфейса?

Переведено автоматически
Ответ 1

Я вижу три решения этой проблемы:


  1. Измените кодировку вывода, чтобы она всегда выводила UTF-8 . Смотрите, Например, Установка правильной кодировки при конвейерном стандартном выводе в Python, но я не смог заставить этот пример работать.


  2. Следующий пример кода позволяет выводить информацию о вашей целевой кодировке.


    # -*- 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 везде, где это необходимо, не делая весь код уродливым.


  3. Сбросьте кодировку вывода глобально в начале работы программного обеспечения:


    На странице 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.

python