Python, Unicode e la console di Windows


146

Quando provo a stampare una stringa Unicode in una console di Windows, visualizzo un UnicodeEncodeError: 'charmap' codec can't encode character ....errore. Suppongo che ciò avvenga perché la console di Windows non accetta caratteri solo Unicode. Qual è il modo migliore per aggirare questo? C'è un modo in cui posso fare in modo che Python stampi automaticamente ?invece di fallire in questa situazione?

Modifica: sto usando Python 2.5.


Nota: @ LasseV.Karlsen la risposta con il segno di spunta è in qualche modo obsoleta (dal 2008). Si prega di utilizzare le soluzioni / risposte / suggerimenti di seguito con cura !!

La risposta di @JFSebastian è più pertinente da oggi (6 gennaio 2016).


Su quale versione di Python sei? Ho visto riferimenti che questo è stato rotto in 2.4.3 e risolto in 2.4.4.
Stu


controllare questo fuori.
Soorena,

1
la risposta più semplice che ho trovato è digitare: chcp 65001 prima di usare pyhton in cmd
Soorena,

1
Quindi dovresti cambiare la risposta accettata ...
Mr_and_Mrs_D

Risposte:


38

Nota: questa risposta è in qualche modo obsoleta (dal 2008). Si prega di utilizzare la soluzione di seguito con cura !!


Ecco una pagina che descrive in dettaglio il problema e una soluzione (cerca nella pagina il testo Wrapping sys.stdout in un'istanza ):

PrintFails - Python Wiki

Ecco un estratto di codice da quella pagina:

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

Ci sono alcune informazioni in più su quella pagina, vale la pena leggere.


7
Il link è morto e l'essenza della risposta non è stata citata. -1
0xC0000022L

1
Quando provo il consiglio dato sull'involucro sys.stdout, stampa le cose sbagliate. Ad esempio, u'\u2013'diventa ûinvece di un trattino.
user2357112 supporta Monica il

@ user2357112 Dovrai pubblicare una nuova domanda a riguardo. Unicode e console di sistema non sono necessariamente la migliore combinazione, ma non ne so abbastanza, quindi se hai bisogno di una risposta definitiva, pubblica qui una domanda su SO.
Lasse V. Karlsen,

2
il collegamento è morto. L'esempio di codice è errato per la console di Windows in cui la tabella codici (OEM) come cp437è diversa dalla tabella codici ANSI di Windows come cp1252. Il codice non risolve l' UnicodeEncodeError: 'charmap' codec can't encode charactererrore e può portare a mojibake, ad esempio, ا©viene silenziosamente sostituito con ╪º⌐.
jfs

73

Aggiornamento: Python 3.6 implementa PEP 528: modifica la codifica della console di Windows in UTF-8 : la console predefinita su Windows ora accetterà tutti i caratteri Unicode. Internamente, utilizza la stessa API Unicode del win-unicode-consolepacchetto indicato di seguito . print(unicode_string)dovrebbe funzionare ora.


Ho ricevuto un UnicodeEncodeError: 'charmap' codec can't encode character... errore

L'errore indica che i caratteri Unicode che si sta tentando di stampare non possono essere rappresentati utilizzando la chcpcodifica dei caratteri della console corrente ( ). La codepage è spesso una codifica a 8 bit come quella cp437che può rappresentare solo ~ 0x100 caratteri da ~ 1M caratteri Unicode:

>>> u "\ N {EURO SIGN}". codifica ('cp437')
Traceback (ultima chiamata più recente):
...
UnicodeEncodeError: il codec 'charmap' non può codificare il carattere '\ u20ac' in posizione 0:
il personaggio è mappato a 

Suppongo che ciò avvenga perché la console di Windows non accetta caratteri solo Unicode. Qual è il modo migliore per aggirare questo?

La console di Windows accetta i caratteri Unicode e può persino visualizzarli (solo BMP) se il carattere corrispondente è configurato . WriteConsoleW()L'API deve essere utilizzata come suggerito nella risposta di @Daira Hopwood . Può essere chiamato in modo trasparente, cioè non è necessario e non è necessario modificare gli script se si utilizza il win-unicode-consolepacchetto :

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

Vedi Qual è il problema con Python 3.4, Unicode, diverse lingue e Windows?

C'è un modo in cui posso fare in modo che Python stampi automaticamente ?invece di fallire in questa situazione?

Se è sufficiente sostituire tutti i caratteri non codificabili con ?nel tuo caso, puoi impostare PYTHONIOENCODINGenvvar :

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

In Python 3.6+, la codifica specificata da PYTHONIOENCODINGenvvar viene ignorata per i buffer della console interattiva a meno che PYTHONLEGACYWINDOWSIOENCODINGenvvar non sia impostato su una stringa non vuota.


3
"la console predefinita su Windows ora accetterà tutti i caratteri Unicode" MA devi configurare la console: fai clic con il tasto destro sulla parte superiore delle finestre (del cmd o dell'IDLE python), in default / font scegli la "console Lucida". (Giapponese e cinese non funzionano per me, ma dovrei sopravvivere senza di essa ...)
JinSnow

2
@Guillaume: la risposta contiene la frase in grassetto sulla console di Windows: "se il carattere corrispondente è configurato". Questa risposta non menziona IDLE, ma non è necessario configurare il tipo di carattere in esso (vedo i caratteri giapponesi e cinesi bene in IDLE per impostazione predefinita. Prova print('\u4E01'), print('\u6b63')).
jfs

2
@Guillaume Puoi anche ottenere cinese se installi il language pack in Windows 10. Ha aggiunto i caratteri della console che supportano il cinese.
Mark Tolonen,

28

Nonostante le altre risposte dal suono plausibile che suggeriscono di cambiare la tabella codici in 65001, ciò non funziona . (Inoltre, modificare la codifica predefinita utilizzando nonsys.setdefaultencoding è una buona idea .)

Vedi questa domanda per dettagli e codice che funziona.


2
win-unicode-consoleIl pacchetto Python (basato sul codice) consente di evitare di modificare lo script se stampa direttamente Unicode usando il py -mrun your_script.pycomando .
jfs,

12

Se non sei interessato a ottenere una rappresentazione affidabile dei personaggi cattivi, potresti usare qualcosa del genere (lavorando con python> = 2.6, incluso 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}")

I caratteri errati nella stringa verranno convertiti in una rappresentazione stampabile dalla console di Windows.


.encode('utf8').decode(sys.stdout.encoding)porta ad esempio mojibake, u"\N{EM DASH}".encode('utf-8').decode('cp437')->ΓÇö
jfs

Semplicemente print(s.encode('utf-8'))può essere un modo migliore per evitare errori del compilatore. Invece, ottieni \ xNN output per caratteri non stampabili, che era abbastanza per i miei messaggi diagnostici.
CODICE REVISIONATO

4
Questo è enormemente, spettacolarmente sbagliata. La codifica in UTF-8 quindi la decodifica come set di caratteri a 8 bit a) spesso falliscono, non tutte le codepage hanno caratteri per tutti i valori di 256 byte eb) sempre un'interpretazione errata dei dati, producendo invece un pasticcio di Mojibake .
Martijn Pieters

10

Il codice seguente renderà l'output di Python su console come UTF-8 anche su Windows.

La console visualizzerà bene i personaggi su Windows 7 ma su Windows XP non li visualizzerà bene, ma almeno funzionerà e, soprattutto, avrai un output coerente dal tuo script su tutte le piattaforme. Sarai in grado di reindirizzare l'output su un file.

Il codice seguente è stato testato con Python 2.6 su 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
C'è un modo per evitarlo usando una console diversa?
endolith

@sorin: Perché prima import win32consoleall'esterno di un trye successivamente lo fai condizionatamente all'interno di un try? Non è quel tipo di inutile (il primo import)
0xC0000022L

Per quello che vale, quello fornito da David-Sarah Hopwood funziona (non riuscivo nemmeno a farlo funzionare perché non mi sono preoccupato di installare il modulo di estensione win32)
Jaykul,

4
Non modificare la codifica predefinita del sistema; correggi invece i tuoi valori Unicode. La modifica della codifica predefinita può interrompere le librerie che si basano sul comportamento predefinito . C'è un motivo per cui devi forzare la ricarica di un modulo prima di poterlo fare.
Martijn Pieters

7

Basta inserire questo codice nella riga di comando prima di eseguire lo script python:

chcp 65001 & set PYTHONIOENCODING=utf-8

5

Come la risposta di Giampaolo Rodolà, ma ancora più sporca: ho davvero, davvero intenzione di passare molto tempo (presto) a comprendere l'intero argomento delle codifiche e come si applicano alle console Windoze,

Per il momento volevo solo sthg, il che significava che il mio programma NON sarebbe CRASH, e che ho capito ... e che non comportava l'importazione di troppi moduli esotici (in particolare sto usando Jython, quindi metà del tempo un Python il modulo risulta in realtà non disponibile).

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

NB "pr" è più breve da digitare rispetto a "print" (e un po 'più breve da digitare rispetto a "safeprint") ...!


Intelligente, un modo rapido e sporco per aggirare il problema. Penso che sia ottimo per una soluzione intermittente.
JFA,

3

Per Python 2 prova:

print unicode(string, 'unicode-escape')

Per Python 3 prova:

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

Oppure prova win-unicode-console:

pip install win-unicode-console
py -mrun your_script.py

2

TL; DR:

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

Mi sono imbattuto in questo me stesso, lavorando su un bot Twitch chat (IRC). (Python 2.7 più recente)

Volevo analizzare i messaggi di chat per rispondere ...

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

ma anche stamparli in modo sicuro sulla console in un formato leggibile dall'uomo:

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

Ciò ha corretto il problema degli UnicodeEncodeError: 'charmap'errori di lancio del bot e ha sostituito i caratteri unicode con ?.


2

La causa del tuo problema NON è che la console Win non è disposta ad accettare Unicode (poiché lo fa poiché immagino Win2k per impostazione predefinita). È la codifica di sistema predefinita. Prova questo codice e vedi cosa ti offre:

import sys
sys.getdefaultencoding()

se dice ascii, c'è la tua causa ;-) Devi creare un file chiamato sitecustomize.py e metterlo nel percorso python (lo metto in /usr/lib/python2.5/site-packages, ma è diverso su Win - è c: \ python \ lib \ site-pacchetti o qualcosa del genere), con i seguenti contenuti:

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

e forse potresti voler specificare anche la codifica nei tuoi file:

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

Modifica: maggiori informazioni sono disponibili nell'eccellente libro Dive into Python


2
setdefaultencoding () non è più lungo in sys (dalla v2.0 secondo i documenti del modulo).
Jon Cage,

Non posso provarlo in questo momento, ma so che ho usato questo trucco in una versione successiva - 2.5 su Windows.
Bartosz Radaczyński,

6
OK, dopo un po 'ho scoperto che: "Questa funzione è intesa per essere utilizzata solo dall'implementazione del modulo del sito e, se necessario, da sitecustomize. Una volta usata dal modulo del sito, viene rimossa dallo spazio dei nomi del modulo sys. "
Bartosz Radaczyński,

4
in realtà è possibile impostare la console di Windows su utf-8. devi dire chcp 65001 e sarà unicode.
Bartosz Radaczyński,

4
Per renderlo assolutamente chiaro: è una pessima idea cambiare la codifica predefinita. Questo è simile a spalking la tua gamba rotta e camminare come se non fosse successo nulla, piuttosto che un medico ha impostato l'osso correttamente. Tutto il codice che gestisce il testo Unicode dovrebbe farlo in modo coerente invece di fare affidamento sulla codifica / decodifica implicita.
Martijn Pieters

1

Un po 'correlato alla risposta di JF Sebastian, ma più diretto.

Se si verifica questo problema durante la stampa sulla console / sul terminale, procedere come segue:

>set PYTHONIOENCODING=UTF-8

3
set PYTHONIOENCODING=UTF-8può portare a mojibake se la console utilizza una codifica diversa come cp437. cp65001ha vari problemi . Per stampare Unicode su console Windows, è necessario utilizzare l'API Unicode ( WriteConsoleW()) come suggerito nella mia risposta, dove PYTHONIOENCODINGviene utilizzato solo per sostituire i caratteri che non possono essere rappresentati nella tabella codici OEM corrente ?( WriteConsoleW()funziona anche per tali caratteri). PYTHONIOENCODINGpuò essere utilizzato se l'output viene reindirizzato a un file.
jfs il

1

Python 3.6 windows7: Esistono diversi modi per avviare un Python che puoi usare la console Python (che ha un logo Python su di essa) o la console Windows (è scritto cmd.exe su di essa).

Non è stato possibile stampare caratteri utf8 nella console di Windows. La stampa di caratteri utf-8 mi dà questo errore:

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 

Dopo aver provato e non aver capito la risposta sopra ho scoperto che era solo un problema di impostazione. Fai clic con il tasto destro sulla parte superiore delle finestre della console cmd, nella scheda fontscegli console lucida.


0

James Sulak ha chiesto,

C'è un modo in cui posso fare in modo che Python stampi automaticamente un? invece di fallire in questa situazione?

Altre soluzioni raccomandano di tentare di modificare l'ambiente Windows o di sostituire la print()funzione di Python . La risposta che segue si avvicina al soddisfacimento della richiesta di Sulak.

In Windows 7, Python 3.5 può essere fatto per stampare Unicode senza lanciare un UnicodeEncodeErrorcome segue:

    Al posto di:     print(text)
    sostituto:     print(str(text).encode('utf-8'))

Invece di generare un'eccezione, Python ora visualizza i caratteri Unicode non stampabili come codici esadecimali \ xNN , ad esempio:

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

Invece di

  Halmalo n'était plus qu'un point noir

Certo, il secondo è preferibile ceteris paribus , ma altrimenti il ​​primo è completamente accurato per i messaggi diagnostici. Poiché visualizza Unicode come valori di byte letterali, il primo può anche aiutare a diagnosticare i problemi di codifica / decodifica.

Nota: la str()chiamata sopra è necessaria perché altrimenti encode()Python rifiuta un carattere Unicode come una tupla di numeri.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.