Python, Unicode i konsola Windows

145

Kiedy próbuję wydrukować ciąg Unicode w konsoli systemu Windows, pojawia się UnicodeEncodeError: 'charmap' codec can't encode character ....błąd. Zakładam, że dzieje się tak, ponieważ konsola systemu Windows nie akceptuje znaków tylko w Unicode. Jaki jest najlepszy sposób obejścia tego? Czy jest jakiś sposób, żebym mógł sprawić, by Python automatycznie drukował ?zamiast niepowodzenia w tej sytuacji?

Edycja: używam Pythona 2.5.


Uwaga: odpowiedź @ LasseV.Karlsen ze znacznikiem wyboru jest nieco nieaktualna (od 2008 r.). Prosimy o ostrożne korzystanie z poniższych rozwiązań / odpowiedzi / sugestii !!

Odpowiedź @JFSebastian jest bardziej aktualna na dzień dzisiejszy (6 stycznia 2016 r.).

James Sulak
źródło
Z jakiej wersji Pythona korzystasz? Widziałem odniesienia, że ​​zostało to zepsute w 2.4.3 i naprawione w 2.4.4.
Stu
3
related: bugs.python.org/issue1602
jfs
sprawdź to .
Soorena
1
najprostszą odpowiedzią, jaką znalazłem, jest wpisanie: chcp 65001 przed użyciem pyhton w cmd
Soorena
1
Następnie powinieneś zmienić zaakceptowaną odpowiedź ...
Mr_and_Mrs_D

Odpowiedzi:

38

Uwaga: ta odpowiedź jest nieco nieaktualna (od 2008 r.). Proszę ostrożnie korzystać z poniższego rozwiązania !!


Oto strona, która szczegółowo opisuje problem i rozwiązanie (wyszukaj na stronie tekst Wrapping sys.stdout into an instance ):

PrintFails - Python Wiki

Oto fragment kodu z tej strony:

$ 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
  Б
  Б

Na tej stronie jest więcej informacji, które warto przeczytać.

Lasse V. Karlsen
źródło
7
Link jest martwy, a istota odpowiedzi nie została zacytowana. -1
0xC0000022L,
1
Kiedy próbuję skorzystać z porady dotyczącej owijania sys.stdout, drukuje niewłaściwe rzeczy. Na przykład, u'\u2013'staje się ûzamiast półpauzu.
user2357112 obsługuje Monikę
@ user2357112 Będziesz musiał zadać nowe pytanie na ten temat. Unicode i konsola systemowa niekoniecznie są najlepszą kombinacją, ale nie wiem o tym wystarczająco dużo, więc jeśli potrzebujesz jednoznacznej odpowiedzi, zadaj tutaj pytanie na ten temat.
Lasse V. Karlsen
2
link nie działa. Przykład kodu jest nieprawidłowy dla konsoli systemu Windows, gdzie strona kodowa (OEM), taka jak, cp437różni się od strony kodowej Windows ANSI, takiej jak cp1252. Kod nie naprawia UnicodeEncodeError: 'charmap' codec can't encode characterbłędu i może prowadzić np. Do mojibake'a, ا©jest po cichu zastępowany przez ╪º⌐.
jfs
73

Aktualizacja: Python 3.6 implementuje PEP 528: Zmień kodowanie konsoli systemu Windows na UTF-8 : domyślna konsola w systemie Windows będzie teraz akceptować wszystkie znaki Unicode. Wewnętrznie używa tego samego API Unicode jak na win-unicode-consoleopakowaniu wymienionym poniżej . print(unicode_string)powinno teraz działać.


Pojawia się UnicodeEncodeError: 'charmap' codec can't encode character... błąd.

Ten błąd oznacza, że ​​znaki Unicode, które próbujesz wydrukować, nie mogą być reprezentowane przy użyciu bieżącego chcpkodowania znaków konsoli. Strona kodowa jest często cp437kodowana 8-bitowo, na przykład może reprezentować tylko ~ 0x100 znaków z ~ 1 M znaków Unicode:

>>> u "\ N {EURO SIGN}". encode ('cp437')
Traceback (ostatnie ostatnie połączenie):
...
UnicodeEncodeError: kodek „charmap” nie może zakodować znaku „\ u20ac” na pozycji 0:
mapy znaków do 

Zakładam, że dzieje się tak, ponieważ konsola systemu Windows nie akceptuje znaków tylko w Unicode. Jaki jest najlepszy sposób obejścia tego?

Konsola systemu Windows akceptuje znaki Unicode, a nawet może je wyświetlać (tylko BMP), jeśli skonfigurowano odpowiednią czcionkę . WriteConsoleW()API należy używać zgodnie z sugestią zawartą w odpowiedzi @Daira Hopwood . Można to nazwać transparentnie, tzn. Nie musisz i nie powinieneś modyfikować swoich skryptów, jeśli używasz win-unicode-consolepakietu :

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

Zobacz O co chodzi z Pythonem 3.4, Unicode, różnymi językami i systemem Windows?

Czy jest jakiś sposób, żebym mógł sprawić, by Python automatycznie drukował ?zamiast niepowodzenia w tej sytuacji?

Jeśli ?w twoim przypadku wystarczy zamienić wszystkie niekodowalne znaki na, możesz ustawić PYTHONIOENCODINGenvvar :

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

W Pythonie PYTHONIOENCODING3.6+ kodowanie określone przez envvar jest ignorowane dla buforów konsoli interaktywnej, chyba że PYTHONLEGACYWINDOWSIOENCODINGenvvar jest ustawiony na niepusty ciąg.

jfs
źródło
3
„domyślna konsola w systemie Windows będzie teraz akceptować wszystkie znaki Unicode”, ALE musisz skonfigurować konsolę: kliknij prawym przyciskiem myszy w górnej części okien (cmd lub python IDLE), domyślnie / font wybierz „Lucida console”. (Japoński i chiński nie działają dla mnie, ale powinienem przeżyć bez tego ...)
JinSnow
2
@Guillaume: odpowiedź zawiera pogrubioną frazę dotyczącą konsoli Windows: „jeśli odpowiednia czcionka jest skonfigurowana”. Ta odpowiedź nie wspomina o IDLE, ale nie musisz konfigurować w nim czcionki (domyślnie widzę znaki japońskie i chińskie w IDLE. Spróbuj print('\u4E01'), print('\u6b63')).
jfs
2
@Guillaume Możesz nawet zdobyć chiński, jeśli zainstalujesz pakiet językowy w systemie Windows 10. Dodano czcionki konsoli obsługujące język chiński.
Mark Tolonen
28

Pomimo innych wiarygodnie brzmiących odpowiedzi, które sugerują zmianę strony kodowej na 65001, to nie działa . (Również, zmieniając domyślne kodowanie za pomocą sys.setdefaultencodingto nie jest dobry pomysł ).

Zobacz to pytanie, aby uzyskać szczegółowe informacje i kod, który działa.

Daira Hopwood
źródło
2
win-unicode-consolePakiet Pythona (oparty na Twoim kodzie) pozwala uniknąć modyfikacji skryptu, jeśli drukuje on Unicode bezpośrednio za pomocą py -mrun your_script.pypolecenia .
jfs
12

Jeśli nie jesteś zainteresowany uzyskaniem wiarygodnej reprezentacji złych znaków, możesz użyć czegoś takiego (praca z pythonem> = 2.6, w tym 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}")

Złe znaki w ciągu zostaną przekonwertowane na reprezentację, którą można wydrukować w konsoli systemu Windows.

Giampaolo Rodolà
źródło
.encode('utf8').decode(sys.stdout.encoding)prowadzi do mojibake, np. u"\N{EM DASH}".encode('utf-8').decode('cp437')->ΓÇö
jfs
Po prostu print(s.encode('utf-8'))może być lepszym sposobem uniknięcia błędów kompilatora. Zamiast tego otrzymujesz wyjście \ xNN dla niedrukowalnych znaków, co wystarczyło dla moich komunikatów diagnostycznych.
CODE-REaD
4
To jest ogromnie, spektakularnie błędne. Kodowanie do UTF-8, a następnie dekodowanie jako 8-bitowego zestawu znaków a) często kończy się niepowodzeniem, nie wszystkie strony kodowe zawierają znaki dla wszystkich 256-bajtowych wartości oraz b) zawsze błędna interpretacja danych, powodując zamiast tego bałagan Mojibake'a .
Martijn Pieters
10

Poniższy kod spowoduje, że dane wyjściowe Pythona będą wyświetlane na konsoli jako UTF-8 nawet w systemie Windows.

Konsola będzie dobrze wyświetlać znaki w systemie Windows 7, ale w systemie Windows XP nie będzie ich dobrze wyświetlać, ale przynajmniej będzie działać i co najważniejsze, będziesz mieć spójne dane wyjściowe ze skryptu na wszystkich platformach. Będziesz mógł przekierować dane wyjściowe do pliku.

Poniższy kod został przetestowany w Pythonie 2.6 w systemie 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"
sorin
źródło
1
Czy można tego uniknąć, używając po prostu innej konsoli?
endolith
@sorin: Dlaczego najpierw na import win32consolezewnątrz a, trya później warunkowo wewnątrz a try? Czy to nie jest bezcelowe (pierwsze import)
0xC0000022L,
Jeśli chodzi o wartość, działa ten dostarczony przez Davida-Sarah Hopwood (nie udało mi się nawet uruchomić tego, ponieważ nie zawracałem sobie
głowy
4
Nie zmieniaj domyślnego kodowania systemu; zamiast tego napraw wartości Unicode. Zmiana domyślnego kodowania może zepsuć biblioteki, które opierają się na domyślnym zachowaniu . Jest powód, dla którego musisz wymusić ponowne załadowanie modułu, zanim będziesz mógł to zrobić.
Martijn Pieters
7

Po prostu wprowadź ten kod w linii poleceń przed wykonaniem skryptu w Pythonie:

chcp 65001 & set PYTHONIOENCODING=utf-8
c97
źródło
5

Podobnie jak odpowiedź Giampaolo Rodolà, ale jeszcze bardziej brudna: naprawdę, naprawdę zamierzam spędzić dużo czasu (wkrótce) na zrozumieniu całego tematu kodowań i ich zastosowania w konsolach Windoze,

W tej chwili chciałem tylko sthg, co oznaczałoby, że mój program NIE ulegnie awarii i co zrozumiałem ... a także nie wymagało importowania zbyt wielu egzotycznych modułów (w szczególności używam Jythona, więc przez połowę czasu Python okazuje się, że w rzeczywistości nie jest dostępny).

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

Uwaga: "pr" jest krótsze niż "print" (i trochę krótsze niż "safeprint") ...!

mike gryzoń
źródło
Sprytny, szybki i brudny sposób na obejście problemu. Myślę, że to świetnie nadaje się do sporadycznego rozwiązania.
JFA,
3

W przypadku Pythona 2 wypróbuj:

print unicode(string, 'unicode-escape')

W przypadku Pythona 3 wypróbuj:

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

Lub wypróbuj win-unicode-console:

pip install win-unicode-console
py -mrun your_script.py
shubaly
źródło
2

TL; DR:

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

Sam napotkałem to, pracując nad botem do czatu na Twitchu (IRC). (Najnowszy Python 2.7)

Chciałem przeanalizować wiadomości na czacie, aby odpowiedzieć ...

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

ale także bezpiecznie wydrukuj je na konsoli w formacie czytelnym dla człowieka:

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

To rozwiązało problem z wyrzucaniem przez bota UnicodeEncodeError: 'charmap'błędów i zastąpiło znaki Unicode na ?.

Matthew Estock
źródło
2

Przyczyną twojego problemu NIE jest konsola Win, która nie chce akceptować Unicode (tak jak to robi, ponieważ domyślam się, że Win2k jest domyślnie). Jest to domyślne kodowanie systemu. Wypróbuj ten kod i zobacz, co daje:

import sys
sys.getdefaultencoding()

jeśli mówi ascii, to jest twoja przyczyna ;-) Musisz utworzyć plik o nazwie sitecustomize.py i umieścić go w ścieżce Pythona (umieściłem go w /usr/lib/python2.5/site-packages, ale to jest różnica Win - to jest c: \ python \ lib \ site-packages czy coś), z następującą zawartością:

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

i być może będziesz chciał określić kodowanie również w swoich plikach:

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

Edycja: więcej informacji można znaleźć w doskonałej książce Dive into Python

Bartosz Radaczyński
źródło
2
setdefaultencoding () nie działa już w sys (od wersji 2.0 zgodnie z dokumentacją modułu).
Jon Cage
Nie mogę tego teraz udowodnić, ale wiem, że użyłem tej sztuczki w późniejszej wersji - 2.5 na Windows.
Bartosz Radaczyński
6
OK, po jakimś czasie dowiedziałem się, że: „Ta funkcja jest przeznaczona wyłącznie do wykorzystania przez implementację modułu witryny i, w razie potrzeby, przez sitecustomize. Po użyciu przez moduł witryny jest ona usuwana z przestrzeni nazw modułu sys. "
Bartosz Radaczyński
4
właściwie możesz ustawić konsolę Windows na utf-8. musisz powiedzieć chcp 65001 i będzie to Unicode.
Bartosz Radaczyński
4
Aby było jasne: bardzo złym pomysłem jest zmiana domyślnego kodowania. Jest to podobne do opryskania złamaną nogą i chodzenia tak, jakby nic się nie stało, zamiast zlecić lekarzowi prawidłowe ustawienie kości. Cały kod obsługujący tekst Unicode powinien to robić konsekwentnie, zamiast polegać na niejawnym kodowaniu / dekodowaniu.
Martijn Pieters
1

Coś podobnego do odpowiedzi JF Sebastiana, ale bardziej bezpośrednie.

Jeśli masz ten problem podczas drukowania do konsoli / terminala, wykonaj następujące czynności:

>set PYTHONIOENCODING=UTF-8
Kinjal Dixit
źródło
3
set PYTHONIOENCODING=UTF-8może prowadzić do mojibake, jeśli konsola używa innego kodowania, takiego jak cp437. cp65001ma różne problemy . Aby wydrukować Unicode na konsoli Windows, należy użyć Unicode API ( WriteConsoleW()), jak zasugerowałem w mojej odpowiedzi, gdzie PYTHONIOENCODINGjest używany tylko do zamiany znaków, których nie można przedstawić na bieżącej stronie kodowej OEM ?( WriteConsoleW()działa nawet dla takich znaków). PYTHONIOENCODINGmoże być używany, jeśli dane wyjściowe są przekierowywane do pliku.
jfs
1

Python 3.6 windows7: Istnieje kilka sposobów na uruchomienie Pythona, możesz użyć konsoli Pythona (która ma logo Pythona) lub konsoli Windows (jest na niej napisane cmd.exe).

Nie mogłem wydrukować znaków utf8 w konsoli systemu Windows. Drukowanie znaków utf-8 wyrzuca mi ten błąd:

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 

Po próbie zrozumienia powyższej odpowiedzi bez zrozumienia stwierdziłem, że był to tylko problem z ustawieniem. Kliknij prawym przyciskiem myszy w górnej części okien konsoli cmd, na karcie fontwybierz konsolę lucida.

J. Robi
źródło
0

James Sulak zapytał,

Czy jest jakiś sposób, aby Python automatycznie drukował? zamiast zawieść w tej sytuacji?

Inne rozwiązania zalecają próbę modyfikacji środowiska Windows lub zastąpienia print()funkcji Pythona . Poniższa odpowiedź zbliża się do spełnienia prośby Sulaka.

W Windows 7, Python 3.5 może drukować Unicode bez rzucania UnicodeEncodeError:

    Zamiast:     print(text)
    substytut:     print(str(text).encode('utf-8'))

Zamiast rzucać wyjątek, Python wyświetla teraz niedrukowalne znaki Unicode jako kody szesnastkowe \ xNN , np .:

  Halmalo n \ xe2 \ x80 \ x99 \ xc3 \ xa9tait plus qu \ xe2 \ x80 \ x99un point noir

Zamiast

  Halmalo n'était plus qu'un point noir

To prawda, że ​​to drugie jest preferowane ceteris paribus , ale poza tym to pierwsze jest całkowicie dokładne w przypadku komunikatów diagnostycznych. Ponieważ wyświetla Unicode jako literalne wartości bajtów, może również pomóc w diagnozowaniu problemów z kodowaniem / dekodowaniem.

Uwaga:str() powyżej konieczne jest wezwanie, ponieważ w przeciwnym razie encode()powoduje Python odrzucić znak Unicode jako krotki liczb.

CODE-REaD
źródło