Python, Unicode y la consola de Windows

146

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

Editar: estoy usando Python 2.5.


Nota: la respuesta de @ LasseV.Karlsen con la marca de verificación está desactualizada (desde 2008). ¡Utilice 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).

James Sulak
fuente
¿En qué versión de Python estás? He visto referencias de que esto se rompió en 2.4.3 y se arregló en 2.4.4.
Stu
3
relacionado: bugs.python.org/issue1602
jfs
comprobar esto a cabo.
Soorena
1
la respuesta más simple que encontré es escribir: chcp 65001 antes de usar pyhton en cmd
Soorena
1
Entonces debería cambiar su respuesta aceptada ...
Mr_and_Mrs_D

Respuestas:

38

Nota: Esta respuesta está desactualizada (desde 2008). ¡Utilice la solución a continuación con cuidado!


Aquí hay una página que detalla el problema y una solución (busque en la página 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
  <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
  Б
  Б

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

Lasse V. Karlsen
fuente
77
El enlace está muerto y la esencia de la respuesta no fue citada. -1
0xC0000022L
1
Cuando pruebo los consejos dados sobre el envoltorio sys.stdout, imprime las cosas incorrectas. Por ejemplo, se u'\u2013'convierte en ûlugar de en-dash.
user2357112 es compatible con Monica el
@ user2357112 Deberá publicar una nueva pregunta al respecto. Unicode y la consola del sistema no son necesariamente la mejor combinación, pero no sé lo suficiente sobre esto, así que si necesita una respuesta definitiva, publique una pregunta aquí en SO al respecto.
Lasse V. Karlsen
2
El enlace está muerto. El ejemplo de código es incorrecto para la consola de Windows donde la página de códigos (OEM) cp437es diferente de la página de códigos ANSI de Windows como cp1252. El código no soluciona UnicodeEncodeError: 'charmap' codec can't encode charactererror y puede conducir a mojibake por ejemplo, ا©está en silencio reemplazado con ╪º⌐.
jfs
73

Actualización: Python 3.6 implementa PEP 528: cambie la codificación de la consola de Windows a UTF-8 : la consola predeterminada en Windows ahora aceptará todos los caracteres Unicode. Internamente, utiliza la misma API Unicode que el win-unicode-consolepaquete mencionado a continuación . print(unicode_string)Debería funcionar ahora.


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

El error significa que los caracteres Unicode que está intentando imprimir no se pueden representar utilizando la chcpcodificación de caracteres de la consola actual ( ). La página de códigos suele ser una codificación de 8 bits, comocp437 que puede representar solo ~ 0x100 caracteres de ~ 1M caracteres Unicode:

>>> u "\ N {SIGNO EURO}". codificar ('cp437')
Rastreo (llamadas recientes más última):
...
UnicodeEncodeError: el códec '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 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 debe usarse como se sugiere en la respuesta de @Daira Hopwood . Se puede llamar de forma transparente, es decir, no es necesario y no debe modificar sus scripts si usa el win-unicode-consolepaquete :

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

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

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

Si es suficiente para reemplazar todos los caracteres no codificables ?en su caso, entonces podría configurar PYTHONIOENCODINGenvvar :

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

En Python 3.6+, la codificación especificada por PYTHONIOENCODINGenvvar se ignora para las memorias intermedias de consola interactivas a menos que PYTHONLEGACYWINDOWSIOENCODINGenvvar esté configurado en una cadena no vacía.

jfs
fuente
3
"la consola predeterminada en Windows ahora aceptará todos los caracteres Unicode" PERO debe configurar la consola: haga clic con el botón derecho en la parte superior de las ventanas (del cmd o el IDLE de python), en la fuente / predeterminado elija la "consola Lucida". (Los japoneses y los chinos no funcionan para mí, pero debería sobrevivir sin ellos ...)
JinSnow
2
@Guillaume: la respuesta contiene la frase en negrita sobre la consola de Windows: "si la fuente correspondiente está configurada". Esta respuesta no menciona IDLE pero no necesita configurar la fuente (veo caracteres japoneses y chinos muy bien en IDLE por defecto. Intente print('\u4E01'), print('\u6b63')).
jfs
2
@Guillaume Incluso puede obtener chino si instala el paquete de idioma en Windows 10. Agregó fuentes de consola que admiten chino.
Mark Tolonen
28

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 codificación predeterminada usando nosys.setdefaultencoding es una buena idea ).

Consulte esta pregunta para obtener detalles y el código que funciona.

Daira Hopwood
fuente
2
win-unicode-consoleEl paquete Python (basado en su código) permite evitar modificar su script si imprime Unicode directamente usando el py -mrun your_script.pycomando .
jfs
12

Si no está interesado en obtener una representación confiable de los caracteres malos, puede usar algo como esto (trabajando con python> = 2.6, incluyendo 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 la consola de Windows pueda imprimir.

Giampaolo Rodolà
fuente
.encode('utf8').decode(sys.stdout.encoding)conduce a mojibake, por ejemplo, u"\N{EM DASH}".encode('utf-8').decode('cp437')->ΓÇö
jfs
Simplemente print(s.encode('utf-8'))puede ser una mejor manera de evitar errores de compilación. En cambio, obtienes \ xNN salida para caracteres no imprimibles, que fue suficiente para mis mensajes de diagnóstico.
CÓDIGO-LEA
44
Esto es enorme, espectacularmente incorrecto. Codificar a UTF-8 y luego decodificar como un juego de caracteres de 8 bits a) a menudo falla, no todas las páginas de códigos tienen caracteres para todos los valores de 256 bytes, yb) siempre la interpretación incorrecta de los datos, produciendo un desastre de Mojibake .
Martijn Pieters
10

El siguiente código hará que Python realice la salida a la consola 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á redirigir la salida a un archivo.

El siguiente código se probó 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"
Sorin
fuente
1
¿Hay alguna manera de evitar esto simplemente usando una consola diferente?
endolito
@sorin: ¿Por qué primero import win32consolefuera de una tryy más tarde lo hace de forma condicional dentro de una try? ¿No es inútil (el primero import)
0xC0000022L
Por lo que vale, funciona el proporcionado por David-Sarah Hopwood (no conseguí que este se ejecutara incluso porque no me he molestado en instalar el módulo de extensiones win32)
Jaykul
44
No cambie la codificación predeterminada del sistema; arregla tus valores Unicode en su lugar. Cambiar la codificación predeterminada puede romper las bibliotecas que dependen del comportamiento predeterminado . Hay una razón por la que debe forzar la recarga de un módulo antes de poder hacer esto.
Martijn Pieters
7

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

chcp 65001 & set PYTHONIOENCODING=utf-8
c97
fuente
5

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

Por el momento solo quería sthg, lo que significaría que mi programa NO CRASH, y lo entendí ... y también que no implicaba importar demasiados módulos exóticos (en particular, estoy usando Jython, así que la mitad del tiempo un Python De hecho, el módulo 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 "print" (y bastante más corto de escribir que "safeprint") ...!

Mike roedor
fuente
Una forma inteligente y rápida de solucionar el problema. Creo que esto es genial para una solución intermitente.
JFA
3

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
shubaly
fuente
2

TL; DR:

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

Me encontré con esto yo mismo, trabajando en un bot de chat Twitch (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 para humanos:

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

Esto corrigió el problema de los UnicodeEncodeError: 'charmap'errores de lanzamiento de bot y reemplazó los caracteres unicode con ?.

Matthew Estock
fuente
2

La causa de su problema NO es que la consola Win no esté dispuesta a aceptar Unicode (como lo hace, ya que supongo que Win2k por defecto). Es la codificación predeterminada del sistema. Pruebe este código y vea lo que le ofrece:

import sys
sys.getdefaultencoding()

si dice ascii, ahí 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 codificación en sus archivos:

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

Editar: se puede encontrar más información en el excelente libro Inmersión en Python

Bartosz Radaczyński
fuente
2
setdefaultencoding () no es más largo en sys (a partir de v2.0 de acuerdo con los documentos del módulo).
Jon Cage el
No puedo probarlo en este momento, pero sé que he usado este truco en una versión posterior: 2.5 en Windows.
Bartosz Radaczyński
66
OK, después de bastante tiempo descubrí que: "Esta función solo debe ser utilizada por la implementación del módulo del sitio y, cuando sea necesario, por el sitio personalizado. Una vez utilizada por el módulo del sitio, se elimina del espacio de nombres del módulo sys. "
Bartosz Radaczyński
44
en realidad puedes configurar la consola de windows para que sea utf-8. necesita decir chcp 65001 y será unicode.
Bartosz Radaczyński
44
Para que quede absolutamente claro: es una muy mala idea cambiar la codificación predeterminada. Esto es similar a provocar su pierna rota y caminar como si nada sucediera, en lugar de que un médico coloque el hueso correctamente. Todo el código que maneja el texto Unicode debe hacerlo consistentemente en lugar de depender de la codificación / decodificación implícita.
Martijn Pieters
1

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
Kinjal Dixit
fuente
3
set PYTHONIOENCODING=UTF-8puede conducir a mojibake si la consola usa una codificación diferente, como cp437. cp65001Tiene varios problemas . Para imprimir Unicode en la consola de Windows, se debe usar la API de Unicode ( WriteConsoleW()) como se sugiere en mi respuesta, donde PYTHONIOENCODINGsolo se usa para reemplazar caracteres que no se pueden representar en la página de códigos OEM actual ?( WriteConsoleW()funciona incluso para tales caracteres). PYTHONIOENCODINGse puede usar si la salida se redirige a un archivo.
jfs
1

Python 3.6 windows7: hay varias formas de iniciar una 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. Imprimir caracteres utf-8 me arroja 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 solo era un problema de configuración. Haga clic derecho en la parte superior de las ventanas de la consola cmd, en la pestaña fontelija la consola lucida.

J. Does
fuente
0

James Sulak preguntó:

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

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

En Windows 7, se puede hacer que Python 3.5 imprima Unicode sin lanzar un de la UnicodeEncodeErrorsiguiente 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 negro

En vez de

  Halmalo n'était plus qu'un point noir

Por supuesto, este último es preferible ceteris paribus , pero por lo demás el primero es completamente preciso 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 codificación / decodificación.

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

LECTURA DE CÓDIGO
fuente