Python, Unicode та консоль Windows


146

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

Редагувати: я використовую Python 2.5.


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

Відповідь @JFSebastian є більш актуальною на сьогодні (6 січня 2016).


На якій версії Python ви працюєте? Я бачив згадки, що це було порушено в 2.4.3 та виправлено в 2.4.4.
Світ

3
пов'язані: bugs.python.org/issue1602
jfs

перевірити це .
Соорена

1
найпростіша відповідь, яку я знайшов, - це набрати: chcp 65001 перед використанням pyhton у cmd
Soorena

1
Тоді вам слід змінити прийняту відповідь ...
Mr_and_Mrs_D

Відповіді:


38

Примітка. Ця відповідь є якось застарілою (з 2008 року). Будь ласка, використовуйте рішення нижче обережно !!


Ось сторінка, яка детально описує проблему та рішення (пошук на сторінці тексту Wrapping 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 підтримує Monica

@ user2357112 Вам потрібно буде написати нове запитання з цього приводу. Unicode та системна консоль - це не обов'язково найкраща комбінація, але я не знаю про це достатньо, тому якщо вам потрібна однозначна відповідь, опублікуйте тут питання на ТАК про це.
Лассе В. Карлсен

2
посилання мертва. Приклад коду неправильний для консолі Windows, де кодова сторінка (OEM), така як cp437відрізняється від кодової сторінки Windows ANSI, наприклад cp1252. Код не виправляє UnicodeEncodeError: 'charmap' codec can't encode characterпомилки і може призвести до mojibake, наприклад, ا©мовчки замінюється ╪º⌐.
jfs

73

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


Я отримую UnicodeEncodeError: 'charmap' codec can't encode character... помилку.

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

>>> u "\ N {EURO SIGN}". encode ('cp437')
Traceback (останній останній дзвінок):
...
UnicodeEncodeError: кодек "charmap" не може кодувати символ "\ u20ac" у позиції 0:
символьні карти до 

Я припускаю, що це тому, що консоль Windows не приймає символи, призначені лише для Unicode. Який найкращий спосіб обійти це?

Консоль Windows приймає символи Unicode і навіть може відображати їх (лише BMP), якщо відповідний шрифт налаштований . WriteConsoleW()API слід використовувати, як пропонується у відповіді @Daira Hopwood . Це можна назвати прозоро, тобто вам не потрібно і не слід змінювати свої сценарії, якщо ви використовуєте win-unicode-consoleпакет :

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

Дивіться, у чому полягає угода з Python 3.4, Unicode, різними мовами та Windows?

Чи є спосіб, щоб я міг змусити Python автоматично друкувати, ?а не відмовлятись у цій ситуації?

Якщо ?у вашому випадку достатньо замінити всі символи, які не можна PYTHONIOENCODINGрозшифрувати, тоді ви можете встановити envvar :

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

У Python 3.6+, кодування, визначене PYTHONIOENCODINGenvvar, ігнорується для інтерактивних консольних буферів, якщо PYTHONLEGACYWINDOWSIOENCODINGenvvar не встановлено на порожню рядок.


3
"консоль за замовчуванням у Windows тепер прийме всі символи Unicode", Але вам потрібно конфігурувати консоль: клацніть правою кнопкою миші вгорі вікон (cmd або пітон IDLE), за замовчуванням / шрифтом виберіть "консоль Lucida". (Японці та китайці не працюють для мене, але я повинен вижити без цього ...)
JinSnow

2
@Guillaume: відповідь містить фразу жирного шрифту про Windows , консолях: «якщо відповідний шрифт налаштований.» Ця відповідь не згадує IDLE, але вам не потрібно налаштовувати шрифт (я вважаю, що японські та китайські символи просто відмінно в IDLE за замовчуванням. Спробуйте print('\u4E01'), print('\u6b63')).
jfs

2
@Guillaume Ви навіть можете отримати китайську мову, якщо встановити мовний пакет в Windows 10. Додано консольні шрифти, що підтримують китайську мову.
Марк Толонен

28

Незважаючи на інші правдоподібні відповіді, які пропонують змінити кодову сторінку на 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 байтів; і б) завжди неправильна інтерпретація даних, створюючи замість них Модабаке .
Martijn Pieters

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
Чи є спосіб уникнути цього просто за допомогою іншої консолі?
ендоліт

@sorin: Чому ви спочатку знаходитесь import win32consoleпоза a, tryа пізніше це робиться умовно всередині try? Хіба це не безглуздо (перше import)
0xC0000022L

Що того, чого варто, працює той, який надав Девід-Сара Хопвуд (я не
змусив

4
Не змінюйте системне кодування за замовчуванням; виправте замість цього значення Unicode. Зміна кодування за замовчуванням може порушити бібліотеки, які покладаються на, знаєте, поведінку за замовчуванням . Існує причина, що вам доведеться змусити перезавантажити модуль, перш ніж це зробити.
Martijn Pieters

7

Просто введіть цей код у командному рядку перед виконанням сценарію python:

chcp 65001 & set PYTHONIOENCODING=utf-8

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

Зверніть увагу: "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 chat (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-пакети чи щось таке) із таким вмістом:

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

і, можливо, ви можете також вказати кодування у своїх файлах:

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

Редагувати: більше інформації можна знайти у чудовій книзі "Занурись у Пітон"


2
setdefaultencoding () є не довшим у sys (станом на v2.0 відповідно до модуля документів).
Джон Кейдж

Я не можу це довести зараз, але я знаю, що я використовував цей трюк у пізнішій версії - 2.5 для Windows.
Bartosz Radaczyński

6
Добре, через деякий час я виявив, що: "Ця функція призначена лише для реалізації модуля сайту та, де це необхідно, для налаштування сайту. Після використання модуля сайту вона видаляється з простору імен модуля sys. "
Bartosz Radaczyński

4
насправді ви можете встановити консоль для Windows utf-8. вам потрібно сказати chcp 65001, і це буде unicode.
Bartosz Radaczyński

4
Щоб зробити це абсолютно зрозумілим: дуже погана ідея змінити кодування за замовчуванням. Це схоже на те, що лупцювати зламану ногу і ходити далі, ніби нічого не сталося, а не як лікар встановив кістку належним чином. Весь текст, що обробляє код Unicode, повинен робити це послідовно, а не покладатися на неявне кодування / декодування.
Martijn Pieters

1

Вигляд пов'язаний з відповіддю Дж. Ф. Себастьяна, але більш прямий.

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

>set PYTHONIOENCODING=UTF-8

3
set PYTHONIOENCODING=UTF-8може призвести до mojibake, якщо консоль використовує інше кодування, наприклад cp437. cp65001має різні питання . Для друку Unicode на консолі Windows слід використовувати API Unicode ( 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 або замінити Python print() функцію . Відповідь нижче наближається до виконання прохання Сулака.

У Windows 7 Python 3.5 можна зробити для друку Unicode, не викидаючи UnicodeEncodeError наступне:

    Замість:    print(text)
    заміна:     print(str(text).encode('utf-8'))

Замість того, щоб викидати виняток, Python тепер відображає недруковані символи Unicode у вигляді \ xNN шістнадцяткових кодів, наприклад:

  Халмало n \ xe2 \ x80 \ x99 \ xc3 \ xa9tait плюс qu \ xe2 \ x80 \ x99un noir point

Замість

  Halmalo n'était плюс qu'un point noir

Зрозуміло, що останній є кращим при інших родах , але в іншому випадку перший є абсолютно точним для діагностичних повідомлень. Оскільки він відображає Unicode як буквальні значення байтів, перший може також допомогти в діагностиці проблем кодування / декодування.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.