Python, Unicode и консоль Windows

149

Когда я пытаюсь напечатать строку Unicode в консоли Windows, я получаю сообщение UnicodeEncodeError: 'charmap' codec can't encode character ....об ошибке. Я предполагаю, что это связано с тем, что консоль Windows не принимает символы только Unicode. Как лучше всего обойти это? Есть ли способ заставить Python автоматически печатать a ?вместо сбоя в этой ситуации?

Изменить: я использую Python 2.5.


Примечание: ответ @ LasseV.Karlsen с галочкой как бы устаревший (с 2008 года). Пожалуйста, используйте приведенные ниже решения / ответы / предложения с осторожностью!

Ответ @JFSebastian более актуален на сегодняшний день (6 января 2016 г.).

Джеймс Сулак
источник
На какой версии Python вы работаете? Я видел упоминания о том, что это было сломано в 2.4.3 и исправлено в 2.4.4.
Стю,
3
связанные: bugs.python.org/issue1602
jfs
проверить это .
Soorena
1
самый простой ответ, который я нашел, - это набрать: chcp 65001 перед использованием pyhton в cmd
Soorena
1
Тогда вы должны изменить принятый ответ ...
Mr_and_Mrs_D

Ответы:

38

Примечание: этот ответ вроде устарел (с 2008 года). Пожалуйста, используйте приведенное ниже решение осторожно!


Вот страница с подробным описанием проблемы и решения (найдите на странице текст « Перенос sys.stdout в экземпляр» ):

PrintFails - Python Wiki

Вот отрывок кода с этой страницы:

$ python -c 'import sys, codecs, locale; print sys.stdout.encoding; \
    sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout); \
    line = u"\u0411\n"; print type(line), len(line); \
    sys.stdout.write(line); print line'
  UTF-8
  <type 'unicode'> 2
  Б
  Б

  $ python -c 'import sys, codecs, locale; print sys.stdout.encoding; \
    sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout); \
    line = u"\u0411\n"; print type(line), len(line); \
    sys.stdout.write(line); print line' | cat
  None
  <type 'unicode'> 2
  Б
  Б

На этой странице есть дополнительная информация, которую стоит прочитать.

Лассе В. Карлсен
источник
7
Ссылка мертва, и суть ответа не цитируется. -1
0xC0000022L
1
Когда я пробую данный совет по упаковке sys.stdout, он печатает не то. Например, u'\u2013'становится ûвместо тире.
user2357112 поддерживает Монику,
@ user2357112 Вам придется задать новый вопрос по этому поводу. Юникод и системная консоль - не обязательно лучшая комбинация, но я недостаточно знаю об этом, поэтому, если вам нужен однозначный ответ, задайте вопрос об этом здесь, в SO.
Лассе В. Карлсен
2
ссылка мертва. Пример кода неверен для консоли Windows, где кодовая страница (OEM), например, cp437отличается от кодовой страницы Windows ANSI, например cp1252. Код не исправляет UnicodeEncodeError: 'charmap' codec can't encode characterошибки и может привести к моджибаке, например, он автоматически ا©заменяется на ╪º⌐.
jfs
76

Обновление: Python 3.6 реализует PEP 528: измените кодировку консоли Windows на UTF-8 : консоль по умолчанию в Windows теперь будет принимать все символы Unicode. Внутри он использует тот же Unicode API, что и win-unicode-consoleупомянутый ниже пакет . print(unicode_string)должен просто работать сейчас.


Я получаю UnicodeEncodeError: 'charmap' codec can't encode character... ошибку

Ошибка означает, что символы Юникода, которые вы пытаетесь напечатать, не могут быть представлены с использованием chcpкодировки символов консоли current ( ). Кодовая страница часто представляет собой 8-битную кодировку, например, cp437которая может представлять только ~ 0x100 символов из ~ 1M символов Unicode:

>>> u "\ N {ЗНАК ЕВРО}". encode ('cp437')
Отслеживание (последний вызов последний):
...
UnicodeEncodeError: кодек 'charmap' не может кодировать символ '\ u20ac' в позиции 0:
символы отображаются на 

Я предполагаю, что это связано с тем, что консоль Windows не принимает символы только Unicode. Как лучше всего обойти это?

Консоль Windows принимает символы Unicode и даже может отображать их (только BMP), если настроен соответствующий шрифт . WriteConsoleW()API следует использовать, как предложено в ответе @Daira Hopwood . Его можно вызывать прозрачно, то есть вам не нужно и не следует изменять свои скрипты, если вы используете win-unicode-consolepackage :

T:\> py -mpip install win-unicode-console
T:\> py -mrun your_script.py

Посмотрите, что такое Python 3.4, Unicode, разные языки и Windows?

Есть ли способ заставить Python автоматически печатать a ?вместо сбоя в этой ситуации?

Если ?в вашем случае достаточно заменить все некодируемые символы на, вы можете установить PYTHONIOENCODINGenvvar :

T:\> set PYTHONIOENCODING=:replace
T:\> python3 -c "print(u'[\N{EURO SIGN}]')"
[?]

В Python 3.6+ кодировка, указанная PYTHONIOENCODINGenvvar, игнорируется для буферов интерактивной консоли, если PYTHONLEGACYWINDOWSIOENCODINGenvvar не установлена ​​в непустую строку.

jfs
источник
4
«консоль по умолчанию в Windows теперь будет принимать все символы Unicode», НО вам необходимо настроить консоль: щелкните правой кнопкой мыши в верхней части окна (cmd или python IDLE), в default / font выберите «Lucida console». (Японский и китайский не работают для меня, но я должен выжить без этого ...)
JinSnow
2
@Guillaume: ответ содержит выделенную жирным шрифтом фразу о консоли Windows: «если настроен соответствующий шрифт». Ответ на этот вопрос не упоминает IDLE , но вам не нужно настроить шрифт в нем (я вижу , японские и китайские иероглифы , просто отлично в IDLE по умолчанию. Попробуйте print('\u4E01'), print('\u6b63')).
jfs
2
@Guillaume Вы даже можете получить китайский язык, если установите языковой пакет в Windows 10. Он добавил консольные шрифты, поддерживающие китайский язык.
Марк Толонен
29

Несмотря на другие правдоподобно звучащие ответы, предлагающие изменить кодовую страницу на 65001, это не работает . (Кроме того , изменение кодировки по умолчанию , используя sys.setdefaultencodingэто не очень хорошая идея .)

См. Этот вопрос для получения подробной информации и кода, который действительно работает.

Дайра Хопвуд
источник
2
win-unicode-consoleПакет Python (на основе вашего кода) позволяет избежать изменения вашего скрипта, если он печатает Unicode напрямую с помощью py -mrun your_script.pyкоманды .
jfs
12

Если вас не интересует надежное представление плохого символа (ов), вы можете использовать что-то вроде этого (работая с python> = 2.6, включая 3.x):

from __future__ import print_function
import sys

def safeprint(s):
    try:
        print(s)
    except UnicodeEncodeError:
        if sys.version_info >= (3,):
            print(s.encode('utf8').decode(sys.stdout.encoding))
        else:
            print(s.encode('utf8'))

safeprint(u"\N{EM DASH}")

Неправильный символ (символы) в строке будет преобразован в представление, которое можно распечатать с помощью консоли Windows.

Джампаоло Родола
источник
.encode('utf8').decode(sys.stdout.encoding)приводит к моджибаке, например, u"\N{EM DASH}".encode('utf-8').decode('cp437')->ΓÇö
jfs
Просто print(s.encode('utf-8'))может быть лучшим способом избежать ошибок компилятора. Вместо этого вы получаете вывод \ xNN для непечатаемых символов, чего было достаточно для моих диагностических сообщений.
CODE-REaD
4
Это в высшей степени неправильно. Кодирование в UTF-8 с последующим декодированием как 8-битной кодировки будет: а) часто терпеть неудачу, не все кодовые страницы имеют символы для всех 256-байтовых значений и б) всегда неверная интерпретация данных, вместо этого возникает беспорядок в стиле моджибаке .
Мартин Питерс
10

Приведенный ниже код будет выводить Python на консоль как UTF-8 даже в Windows.

Консоль будет хорошо отображать символы в Windows 7, но в Windows XP она не будет хорошо их отображать, но, по крайней мере, она будет работать, и, что наиболее важно, у вас будет согласованный вывод вашего скрипта на всех платформах. Вы сможете перенаправить вывод в файл.

Код ниже был протестирован с Python 2.6 в Windows.


#!/usr/bin/python
# -*- coding: UTF-8 -*-

import codecs, sys

reload(sys)
sys.setdefaultencoding('utf-8')

print sys.getdefaultencoding()

if sys.platform == 'win32':
    try:
        import win32console 
    except:
        print "Python Win32 Extensions module is required.\n You can download it from https://sourceforge.net/projects/pywin32/ (x86 and x64 builds are available)\n"
        exit(-1)
    # win32console implementation  of SetConsoleCP does not return a value
    # CP_UTF8 = 65001
    win32console.SetConsoleCP(65001)
    if (win32console.GetConsoleCP() != 65001):
        raise Exception ("Cannot set console codepage to 65001 (UTF-8)")
    win32console.SetConsoleOutputCP(65001)
    if (win32console.GetConsoleOutputCP() != 65001):
        raise Exception ("Cannot set console output codepage to 65001 (UTF-8)")

#import sys, codecs
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
sys.stderr = codecs.getwriter('utf8')(sys.stderr)

print "This is an Е乂αmp١ȅ testing Unicode support using Arabic, Latin, Cyrillic, Greek, Hebrew and CJK code points.\n"
Сорин
источник
1
Есть ли способ избежать этого, просто используя другую консоль?
endolith
@sorin: Почему вы сначала делаете это import win32consoleвне a, tryа потом делаете это условно внутри try? Разве это не бессмысленно (первое import)
0xC0000022L
Как бы то ни было, работает тот, который предоставлен Дэвидом-Сарой Хопвуд (я не заставил его даже запустить, потому что я не удосужился установить модуль расширений win32)
Jaykul
4
Не меняйте кодировку системы по умолчанию; вместо этого исправьте свои значения Unicode. Изменение кодировки по умолчанию может привести к поломке библиотек, которые полагаются на поведение по умолчанию . Есть причина, по которой вы должны принудительно перезагрузить модуль, прежде чем вы сможете это сделать.
Мартейн Питерс
7

Просто введите этот код в командную строку перед выполнением скрипта python:

chcp 65001 & set PYTHONIOENCODING=utf-8
c97
источник
5

Как и ответ Джампаоло Родолы, но еще более грязный: я действительно, действительно намерен потратить много времени (скоро) на понимание всего предмета кодирования и того, как они применяются к консолям Windoze,

На данный момент я просто хотел sthg, что означало бы, что моя программа НЕ БУДЕТ СБОЙ, и что я понял ... а также не предполагал импорта слишком многих экзотических модулей (в частности, я использую Jython, поэтому в половине случаев Python модуль оказывается недоступным).

def pr(s):
    try:
        print(s)
    except UnicodeEncodeError:
        for c in s:
            try:
                print( c, end='')
            except UnicodeEncodeError:
                print( '?', end='')

NB «pr» короче для ввода, чем «print» (и немного короче для ввода, чем «safeprint») ...!

Майк грызун
источник
Умный, быстрый и грязный способ обойти проблему. Я думаю, что это отлично подходит для временного решения.
JFA
3

Для Python 2 попробуйте:

print unicode(string, 'unicode-escape')

Для Python 3 попробуйте:

import os
string = "002 Could've Would've Should've"
os.system('echo ' + string)

Или попробуйте win-unicode-console:

pip install win-unicode-console
py -mrun your_script.py
шубалы
источник
2

TL; DR:

print(yourstring.encode('ascii','replace'));

Я столкнулся с этим сам, работая над ботом чата Twitch (IRC). (Последняя версия Python 2.7)

Я хотел разобрать сообщения чата, чтобы ответить ...

msg = s.recv(1024).decode("utf-8")

но также безопасно распечатать их на консоли в удобочитаемом формате:

print(msg.encode('ascii','replace'));

Это устранило проблему с UnicodeEncodeError: 'charmap'ошибками, возникающими из-за бросков, и заменило символы Unicode на ?.

Мэтью Эсток
источник
2

Причина вашей проблемы НЕ в том, что консоль Win не желает принимать Unicode (поскольку она делает это, поскольку я предполагаю, что Win2k по умолчанию). Это системная кодировка по умолчанию. Попробуйте этот код и посмотрите, что он вам дает:

import sys
sys.getdefaultencoding()

если он говорит ascii, это ваша причина ;-) Вы должны создать файл с именем sitecustomize.py и поместить его по пути python (я поместил его в /usr/lib/python2.5/site-packages, но это отличается от Win - это c: \ python \ lib \ site-packages или что-то в этом роде) со следующим содержимым:

import sys
sys.setdefaultencoding('utf-8')

и, возможно, вы также можете указать кодировку в своих файлах:

# -*- coding: UTF-8 -*-
import sys,time

Изменить: дополнительную информацию можно найти в отличной книге Dive into Python

Бартош Радачиньски
источник
2
setdefaultencoding () больше не используется в sys (начиная с версии 2.0 в соответствии с документацией модуля).
Джон Кейдж,
Я не могу доказать это прямо сейчас, но знаю, что использовал этот трюк в более поздней версии - 2.5 в Windows.
Бартош Радачинский,
6
Хорошо, через некоторое время я обнаружил, что: «Эта функция предназначена только для использования реализацией модуля сайта и, где необходимо, настройкой сайта. После использования модулем сайта она удаляется из пространства имен модуля sys. "
Бартош Радачиньски
4
на самом деле вы можете установить консоль Windows как utf-8. вам нужно сказать chcp 65001, и это будет unicode.
Бартош Радачиньски
4
Чтобы было понятно: менять кодировку по умолчанию - очень плохая идея . Это похоже на то, как если бы врач проткнул сломанную ногу и продолжал ходить, как будто ничего не произошло, как следует. Весь код, обрабатывающий текст Unicode, должен делать это последовательно, а не полагаться на неявное кодирование / декодирование.
Мартейн Питерс
1

Отчасти связано с ответом Дж. Ф. Себастьяна, но более прямым.

Если у вас возникла эта проблема при печати на консоль / терминал, сделайте следующее:

>set PYTHONIOENCODING=UTF-8
Кинджал Диксит
источник
3
set PYTHONIOENCODING=UTF-8может привести к моджибаке, если консоль использует другую кодировку, например cp437. cp65001есть разные проблемы . Чтобы распечатать Unicode на консоли Windows, следует использовать Unicode API ( WriteConsoleW()), как было предложено в моем ответе, где PYTHONIOENCODINGон используется только для замены символов, которые не могут быть представлены на текущей кодовой странице OEM ?( WriteConsoleW()работает даже для таких символов). PYTHONIOENCODINGможет использоваться, если вывод перенаправляется в файл.
jfs
1

Python 3.6 windows7: есть несколько способов запустить python, вы можете использовать консоль python (на которой есть логотип python) или консоль Windows (на ней написано cmd.exe).

Я не мог печатать символы utf8 в консоли Windows. Печать символов utf-8 вызывает у меня эту ошибку:

OSError: [winError 87] The paraneter is incorrect 
Exception ignored in: (_io-TextIOwrapper name='(stdout)' mode='w' ' encoding='utf8') 
OSError: [WinError 87] The parameter is incorrect 

Попытавшись и не сумев понять приведенный выше ответ, я обнаружил, что это всего лишь проблема с настройками. Щелкните правой кнопкой мыши в верхней части окна консоли cmd, на вкладке выберите fontконсоль lucida.

Дж. Ли
источник
0

Джеймс Сулак спросил:

Есть ли способ заставить Python автоматически печатать? вместо того, чтобы потерпеть неудачу в этой ситуации?

Другие решения рекомендуют попытаться изменить среду Windows или заменить print()функцию Python . Ответ ниже приближается к выполнению запроса Сулака.

В Windows 7 можно заставить Python 3.5 печатать Unicode без использования UnicodeEncodeErrorследующего символа :

    Вместо:     print(text)
    замените:     print(str(text).encode('utf-8'))

Вместо выдачи исключения Python теперь отображает непечатаемые символы Unicode в виде шестнадцатеричных кодов \ xNN , например:

  Халмало n \ xe2 \ x80 \ x99 \ xc3 \ xa9tait plus qu \ xe2 \ x80 \ x99un поинт нуар

Вместо того

  Halmalo n'était plus qu'un point noir

Разумеется, последнее предпочтительнее при прочих равных условиях , но в остальном первое является полностью точным для диагностических сообщений. Поскольку он отображает Unicode как буквальные байтовые значения, первый также может помочь в диагностике проблем кодирования / декодирования.

Примечание:str() вызов выше необходимо , потому что в противном случае encode()приводит к Python , чтобы отклонить символ Unicode как кортеж чисел.

КОД-REaD
источник