Python, Unicode y la consola de Windows

Cuando bash imprimir una cadena Unicode en una consola de Windows, obtengo un UnicodeEncodeError: 'charmap' codec can't encode character .... error. Supongo que esto se debe a que la consola de Windows no acepta caracteres solo de Unicode. ¿Cuál es la mejor manera de evitar esto? ¿Hay alguna manera de hacer que Python imprima automáticamente un ? En lugar de fallar en esta situación?

Edición: estoy usando Python 2.5.


Nota: La respuesta de @ LasseV.Karlsen con la marca de verificación está un poco desactualizada (desde 2008). Por favor, use las soluciones / respuestas / sugerencias a continuación con cuidado!

La respuesta de @JFSebastian es más relevante a partir de hoy (6 de enero de 2016).

Nota: Esta respuesta está desactualizada (de 2008). Por favor, use la solución a continuación con cuidado!


Aquí hay una página que detalla el problema y una solución (busque el texto Wrapping sys.stdout en una instancia ):

PrintFails – Python Wiki

Aquí hay un extracto de código de esa página:

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

Hay más información en esa página, vale la pena leerla.

Actualización: Python 3.6 implementa PEP 528: Cambia la encoding de la consola de Windows a UTF-8 : la consola predeterminada en Windows ahora aceptará todos los caracteres Unicode. Internamente, utiliza la misma API de Unicode que el paquete win-unicode-console que se menciona a continuación . print(unicode_string) debería funcionar ahora.


Obtengo un UnicodeEncodeError: 'charmap' codec can't encode character... error.

El error significa que los caracteres Unicode que intenta imprimir no se pueden representar mediante la encoding de caracteres de la consola actual ( chcp ). La página de códigos suele ser una encoding de 8 bits, como cp437 que puede representar solo ~ 0x100 caracteres de ~ 1M caracteres Unicode:

  >>> u "\ N {SIGNO EURO}". codificar ('cp437')
 Rastreo (llamadas recientes más última):
 ...
 UnicodeEncodeError: el codec 'charmap' no puede codificar el carácter '\ u20ac' en la posición 0:
 mapas de caracteres para 

Supongo que esto se debe a que la consola de Windows no acepta caracteres solo de Unicode. ¿Cuál es la mejor manera de evitar esto?

La consola de Windows acepta caracteres Unicode e incluso puede mostrarlos (solo BMP) si la fuente correspondiente está configurada . WriteConsoleW() API de WriteConsoleW() debe usarse como se sugiere en la respuesta de @Daira Hopwood . Puede llamarse de forma transparente, es decir, no necesita y no debe modificar sus scripts si utiliza el paquete win-unicode-console :

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

Consulte ¿Cuál es el problema con Python 3.4, Unicode, diferentes idiomas y Windows?

¿Hay alguna manera de hacer que Python imprima automáticamente un ? En lugar de fallar en esta situación?

Si es suficiente para reemplazar todos los caracteres no codificables con ? En su caso, entonces podría establecer PYTHONIOENCODING PYTHONIOENCODING :

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

En Python 3.6+, la encoding especificada por el PYTHONIOENCODING PYTHONIOENCODING se ignora para los buffers de la consola interactiva a menos que el PYTHONLEGACYWINDOWSIOENCODING PYTHONLEGACYWINDOWSIOENCODING esté establecido en una cadena no vacía.

A pesar de las otras respuestas de sonido plausible que sugieren cambiar la página de códigos a 65001, eso no funciona . (Además, cambiar la encoding predeterminada utilizando sys.setdefaultencoding no es una buena idea ).

Vea esta pregunta para obtener detalles y el código que sí funciona.

Si no estás interesado en obtener una representación confiable de los caracteres incorrectos, puedes usar algo como esto (trabajar con python> = 2.6, incluido 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}") 

Los caracteres incorrectos en la cadena se convertirán en una representación que puede imprimir la consola de Windows.

El siguiente código hará que la salida de Python se consiga como UTF-8 incluso en Windows.

La consola mostrará bien los caracteres en Windows 7, pero en Windows XP no los mostrará bien, pero al menos funcionará y, lo más importante, tendrá una salida consistente de su script en todas las plataformas. Podrás redirigir la salida a un archivo.

A continuación se probó el código con Python 2.6 en 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" 

Como la respuesta de Giampaolo Rodolà, pero aún más sucia: realmente, realmente pretendo pasar mucho tiempo (pronto) comprendiendo todo el tema de las codificaciones y cómo se aplican a las consolas Windoze.

Por el momento solo quería algo, lo que significaría que mi progtwig NO CRUCARÍA, y que entendí … y también que no implicaba importar demasiados módulos exóticos (en particular, estoy usando Jython, así que la mitad de las veces un Python). El módulo resulta que, de hecho, no está disponible).

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

NB: “pr” es más corto de escribir que “imprimir” (y un poco más corto de escribir que “safeprint”) …!

Para Python 2 intente:

 print unicode(string, 'unicode-escape') 

Para Python 3 intente:

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

O prueba win-unicode-console:

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

Simplemente ingrese este código en la línea de comandos antes de ejecutar el script de Python:

 chcp 65001 & set PYTHONIOENCODING=utf-8 

La causa de su problema NO es que la consola Win no esté dispuesta a aceptar Unicode (ya que lo hace ya que supongo que Win2k por defecto). Es la encoding del sistema por defecto. Prueba este código y mira lo que te da:

 import sys sys.getdefaultencoding() 

si dice ascii, está tu causa 😉 Tienes que crear un archivo llamado sitecustomize.py y ponerlo en la ruta de python (lo puse en /usr/lib/python2.5/site-packages, pero eso es diferente en Win – es c: \ python \ lib \ site-packages o algo así, con el siguiente contenido:

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

y quizás también desee especificar la encoding en sus archivos:

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

Edición: se puede encontrar más información en el excelente libro Dive into Python

TL; DR:

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

Me encontré con esto, trabajando en un bot de Twitch chat (IRC). (Python 2.7 más reciente)

Quería analizar los mensajes de chat para responder …

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

pero también imprímalos de forma segura en la consola en un formato legible por humanos:

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

Esto corrigió el problema del bot lanzando UnicodeEncodeError: 'charmap' errores UnicodeEncodeError: 'charmap' y reemplazó los caracteres Unicode con ? .

Algo relacionado en la respuesta de JF Sebastian, pero más directo.

Si tiene este problema al imprimir en la consola / terminal, haga lo siguiente:

 >set PYTHONIOENCODING=UTF-8 

Python 3.6 windows7: hay varias formas de iniciar python: puede usar la consola de python (que tiene un logotipo de python) o la consola de windows (está escrito cmd.exe).

No pude imprimir caracteres utf8 en la consola de Windows. Imprimiendo caracteres utf-8 me lanza este error:

 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 

Después de intentar y no entender la respuesta anterior, descubrí que era solo un problema de configuración. Haga clic con el botón derecho en la parte superior de las ventanas de la consola cmd, en la font la pestaña, seleccione lucida console.

James Sulak preguntó,

¿Hay alguna manera de hacer que Python imprima automáticamente un? En lugar de fallar en esta situación?

Otras soluciones recomiendan que intentemos modificar el entorno de Windows o reemplazar la función print() Python. La respuesta a continuación se acerca al cumplimiento de la solicitud de Sulak.

Bajo Windows 7, Python 3.5 se puede hacer para imprimir Unicode sin lanzar un UnicodeEncodeError siguiente manera:

En lugar de: print(text)
sustituto: print(str(text).encode('utf-8'))

En lugar de lanzar una excepción, Python ahora muestra caracteres Unicode no imprimibles como códigos hexadecimales \ xNN , por ejemplo:

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

En lugar de

Halmalo n’était plus qu’un point noir

Por supuesto, el último es preferible ceteris paribus , pero de lo contrario el primero es completamente exacto para los mensajes de diagnóstico. Debido a que muestra Unicode como valores de bytes literales, el primero también puede ayudar a diagnosticar problemas de encoding / deencoding.

Nota: la llamada str() anterior es necesaria porque de lo contrario, encode() hace que Python rechace un carácter Unicode como una tupla de números.