¿Cambiar la codificación predeterminada de Python?

143

Tengo muchos problemas de "no puedo codificar" y "no puedo decodificar" con Python cuando ejecuto mis aplicaciones desde la consola. Pero en Eclipse PyDev IDE, la codificación de caracteres predeterminada está establecida en UTF-8 , y estoy bien.

Busqué la configuración de la codificación predeterminada, y la gente dice que Python elimina la sys.setdefaultencodingfunción en el inicio, y no podemos usarla.

Entonces, ¿cuál es la mejor solución para ello?

Ali Nadalizadeh
fuente
1
Vea la publicación del blog The Illusive setdefaultencoding .
djc
3
The best solution is to learn to use encode and decode correctly instead of using hacks.Esto fue ciertamente posible con python2 a costa de recordar siempre hacerlo / usar constantemente su propia interfaz. Mi experiencia sugiere que esto se vuelve muy problemático cuando escribes código que quieres trabajar con python2 y python3.
Att Righ

Respuestas:

159

Aquí hay un método más simple (pirateo) que le devuelve la setdefaultencoding()función que se eliminó de sys:

import sys
# sys.setdefaultencoding() does not exist, here!
reload(sys)  # Reload does the trick!
sys.setdefaultencoding('UTF8')

(Nota para Python 3.4+: reload()está en la importlibbiblioteca).

Sin embargo, esto no es algo seguro : esto es obviamente un hack, ya que sys.setdefaultencoding()se elimina a propósito de syscuando se inicia Python. Volver a habilitarlo y cambiar la codificación predeterminada puede romper el código que depende de que ASCII sea el predeterminado (este código puede ser de terceros, lo que generalmente haría que corregirlo sea imposible o peligroso).

Eric O Lebigot
fuente
55
Voté en contra, porque esa respuesta no ayuda a ejecutar aplicaciones existentes (que es una forma de interpretar la pregunta), es incorrecta cuando escribes / mantienes una aplicación y es peligrosa cuando escribes una biblioteca. La forma correcta es establecer LC_CTYPE(o en una aplicación, verificar si está configurado correctamente y abortar con un mensaje de error significativo).
ibotty
@ibotty Estoy de acuerdo en que esta respuesta es un truco y que es peligroso usarla. Sin embargo, responde a la pregunta ("¿Cambiar la codificación predeterminada de Python?"). ¿Tiene alguna referencia sobre el efecto de la variable de entorno LC_CTYPE en el intérprete de Python?
Eric O Lebigot
bueno, no mencionó, es un truco al principio. Aparte de eso, las respuestas peligrosas que no tienen ninguna mención de que son, no son útiles.
ibotty
1
@EOL tienes razón. Sin embargo, sí efectúa la LC_CTYPE=C python -c 'import locale; print( locale.getpreferredencoding())'
codificación
1
@ user2394901 ¡Siempre se desaconseja el uso de sys.setdefaultencoding ()! Y la codificación de py3k está conectada a "utf-8" y cambiarla genera un error.
Marlon Abeykoon
70

Si obtiene este error cuando intenta canalizar / redirigir la salida de su script

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

Simplemente exporte PYTHONIOENCODING en la consola y luego ejecute su código.

export PYTHONIOENCODING=utf8

iman
fuente
3
Esta es la única solución que marcó la diferencia para mí. - Estoy en Debian 7, con configuraciones regionales rotas. Gracias.
Pryo
44
Establezca LC_CTYPEalgo sensato en su lugar. También hace felices a todos los demás programas.
ibotty
55
Un error más grande en Python3 es que PYTHONIOENCODING=utf8no es el predeterminado. Esto hace que los guiones se rompan solo porqueLC_ALL=C
Tino
Set LC_CTYPE to something sensible insteadEsta es una sugerencia razonable. Esto no funciona tan bien cuando intenta distribuir código que solo funciona en el sistema de otra persona.
Att Righ
Los sistemas operativos Debian y Redhat usan un C.utf8entorno local para proporcionar C. glibc upstream más sensible está trabajando para agregarlo, por lo que quizás no deberíamos culpar a Python por respetar la configuración regional \ ...?
Arthur2e5
52

A) Para controlar la sys.getdefaultencoding()salida:

python -c 'import sys; print(sys.getdefaultencoding())'

ascii

Luego

echo "import sys; sys.setdefaultencoding('utf-16-be')" > sitecustomize.py

y

PYTHONPATH=".:$PYTHONPATH" python -c 'import sys; print(sys.getdefaultencoding())'

utf-16-be

Podrías poner tu sitecustomize.py más alto en tu PYTHONPATH.

También te gustaría probar reload(sys).setdefaultencoding con @EOL

B) Para controlar stdin.encodingy stdout.encodingdesea configurar PYTHONIOENCODING:

python -c 'import sys; print(sys.stdin.encoding, sys.stdout.encoding)'

ascii ascii

Luego

PYTHONIOENCODING="utf-16-be" python -c 'import sys; 
print(sys.stdin.encoding, sys.stdout.encoding)'

utf-16-be utf-16-be

Finalmente: ¡puedes usar A) o B) o ambos!

lukmdo
fuente
(solo python2) separado pero interesante se extiende arriba con from __future__ import unicode_literalsver discusión
lukmdo
17

A partir de PyDev 3.4.1, la codificación predeterminada ya no se cambia. Vea este boleto para más detalles.

Para versiones anteriores, una solución es asegurarse de que PyDev no se ejecute con UTF-8 como codificación predeterminada. En Eclipse, ejecute la configuración de diálogo ("ejecutar configuraciones", si no recuerdo mal); puede elegir la codificación predeterminada en la pestaña común. Cámbielo a US-ASCII si desea tener estos errores 'temprano' (en otras palabras: en su entorno PyDev). También vea una publicación de blog original para esta solución .

ChristopheD
fuente
1
Gracias Chris Especialmente considerando el comentario anterior de Mark T, su respuesta parece ser la más apropiada para mí. Y para alguien que no es principalmente un usuario de Eclipse / PyDev, nunca lo habría descubierto por mi cuenta.
Sean
Me gustaría cambiar esto globalmente (en lugar de una vez por configuración de ejecución), pero no he descubierto cómo, he pedido una q separada: stackoverflow.com/questions/9394277/…
Tim Diggins
13

Con respecto a python2 (y solo python2), algunas de las respuestas anteriores se basan en el uso del siguiente truco:

import sys
reload(sys)  # Reload is a hack
sys.setdefaultencoding('UTF8')

Se desaconseja usarlo (marque esto o esto )

En mi caso, viene con un efecto secundario: estoy usando las notebooks ipython, y una vez que ejecuto el código, la función 'imprimir' ya no funciona. Supongo que habría una solución, pero aún creo que usar el hack no debería ser la opción correcta.

Después de probar muchas opciones, la que funcionó para mí fue usar el mismo código en sitecustomize.py, donde ese fragmento de código debe ser . Después de evaluar ese módulo, la función setdefaultencoding se elimina de sys.

Entonces, la solución es agregar el archivo /usr/lib/python2.7/sitecustomize.pydel código:

import sys
sys.setdefaultencoding('UTF8')

Cuando uso virtualenvwrapper, el archivo que edito es ~/.virtualenvs/venv-name/lib/python2.7/sitecustomize.py .

Y cuando lo uso con portátiles Python y conda, es ~/anaconda2/lib/python2.7/sitecustomize.py

Kiril
fuente
8

Hay una publicación de blog perspicaz al respecto.

Ver https://anonbadger.wordpress.com/2015/06/16/why-sys-setdefaultencoding-will-break-code/ .

Parafraseo su contenido a continuación.

En Python 2, que no estaba tan fuertemente tipado con respecto a la codificación de cadenas, podría realizar operaciones en cadenas codificadas de manera diferente y tener éxito. Por ejemplo, lo siguiente volvería True.

u'Toshio' == 'Toshio'

Eso se mantendría para cada cadena (normal, no prefijada) que se codificó sys.getdefaultencoding(), que se estableció de manera predeterminada ascii, pero no en otras.

La codificación predeterminada debía cambiarse en todo el sistema en site.py , pero no en otro lugar. Los hacks (también presentados aquí) para configurarlo en módulos de usuario fueron solo eso: hacks, no la solución.

Python 3 cambió la codificación del sistema por defecto a utf-8 (cuando LC_CTYPE es compatible con Unicode), pero el problema fundamental se resolvió con el requisito de codificar explícitamente cadenas "byte" cada vez que se usan con cadenas Unicode.

ibotty
fuente
4

Primero: reload(sys)y establecer una codificación predeterminada aleatoria con respecto a la necesidad de un flujo de terminal de salida es una mala práctica.reloada menudo cambia cosas en sys que se han implementado en función del entorno, por ejemplo, sys.stdin / stdout streams, sys.excepthook, etc.

Resolviendo el problema de codificación en stdout

La mejor solución que conozco para resolver el problema de codificación de printlas cadenas unicode y más allá de ascii str(por ejemplo, de literales) en sys.stdout es: cuidar un sys.stdout (objeto similar a un archivo) que sea capaz y opcionalmente tolerante con respecto a las necesidades:

  • Cuando sys.stdout.encodingsea Nonepor alguna razón, o no exista, o sea erróneamente falso o "menor" de lo que realmente es capaz el terminal o flujo stdout, intente proporcionar un .encodingatributo correcto . Por fin reemplazándolo sys.stdout & sys.stderrpor un objeto similar a un archivo de traducción.

  • Cuando el terminal / flujo todavía no puede codificar todos los caracteres unicode que ocurren, y cuando no desea romperlos printsolo por eso, puede introducir un comportamiento de codificar con reemplazar en el objeto similar a un archivo de traducción.

Aquí un ejemplo:

#!/usr/bin/env python
# encoding: utf-8
import sys

class SmartStdout:
    def __init__(self, encoding=None, org_stdout=None):
        if org_stdout is None:
            org_stdout = getattr(sys.stdout, 'org_stdout', sys.stdout)
        self.org_stdout = org_stdout
        self.encoding = encoding or \
                        getattr(org_stdout, 'encoding', None) or 'utf-8'
    def write(self, s):
        self.org_stdout.write(s.encode(self.encoding, 'backslashreplace'))
    def __getattr__(self, name):
        return getattr(self.org_stdout, name)

if __name__ == '__main__':
    if sys.stdout.isatty():
        sys.stdout = sys.stderr = SmartStdout()

    us = u'aouäöüфżß²'
    print us
    sys.stdout.flush()

Uso de literales de cadena simple más allá de ascii en código Python 2/2 + 3

Creo que la única buena razón para cambiar la codificación predeterminada global (solo a UTF-8) se refiere a una decisión del código fuente de la aplicación , y no debido a problemas de codificación de flujo de E / S: para escribir literales de cadena más allá de ASCII en el código sin ser forzado usar siempre el u'string'estilo de escape unicode. Esto se puede hacer de manera bastante consistente (a pesar de lo que dice el artículo de anonbadger ) cuidando una base de código fuente Python 2 o Python 2 + 3 que usa los literales de cadena simple ascii o UTF-8 de manera consistente, siempre que esas cadenas se sometan potencialmente en silencio conversión unicode y moverse entre módulos o potencialmente ir a stdout. Para eso, prefiere "# encoding: utf-8"o ascii (sin declaración). Cambiar o descartar las bibliotecas que todavía dependen de una manera muy tonta fatalmente de los errores de codificación predeterminados de ascii más allá del chr # 127 (que es raro hoy en día).

Y haga esto al inicio de la aplicación (y / o a través de sitecustomize.py) además del SmartStdoutesquema anterior, sin usar reload(sys):

...
def set_defaultencoding_globally(encoding='utf-8'):
    assert sys.getdefaultencoding() in ('ascii', 'mbcs', encoding)
    import imp
    _sys_org = imp.load_dynamic('_sys_org', 'sys')
    _sys_org.setdefaultencoding(encoding)

if __name__ == '__main__':
    sys.stdout = sys.stderr = SmartStdout()
    set_defaultencoding_globally('utf-8') 
    s = 'aouäöüфżß²'
    print s

De esta manera, los literales de cadena y la mayoría de las operaciones (excepto la iteración de caracteres) funcionan cómodamente sin pensar en la conversión unicode como si solo hubiera Python3. La E / S de archivos, por supuesto, siempre necesita un cuidado especial con respecto a las codificaciones, como sucede en Python3.

Nota: las cadenas simples se convierten implícitamente de utf-8 a unicode SmartStdoutantes de convertirse a la secuencia de salida encontrando.

kxr
fuente
4

Este es el enfoque que utilicé para producir código que fuera compatible con python2 y python3 y que siempre produjera salida utf8 . Encontré esta respuesta en otra parte, pero no recuerdo la fuente.

Este enfoque funciona reemplazando sys.stdoutcon algo que no es como un archivo (pero que solo usa cosas en la biblioteca estándar). Esto puede causar problemas para sus bibliotecas subyacentes, pero en el caso simple de que tenga un buen control sobre cómo se usa sys.stdout out a través de su marco, este puede ser un enfoque razonable.

sys.stdout = io.open(sys.stdout.fileno(), 'w', encoding='utf8')
Att Righ
fuente
3

Esto solucionó el problema para mí.

import os
os.environ["PYTHONIOENCODING"] = "utf-8"
twasbrillig
fuente
1

Este es un truco rápido para cualquiera que esté (1) en una plataforma de Windows (2) ejecutando Python 2.7 y (3) molesto porque es una buena pieza de software (es decir, no está escrito por usted, por lo que no es un candidato inmediato para la impresión de codificación / decodificación) maniobras) no mostrará los "caracteres bastante unicode" en el entorno IDLE (Pythonwin imprime unicode bien), por ejemplo, los símbolos de lógica de primer orden que Stephan Boyer usa en la salida de su probador pedagógico en First Order Logic Prover .

No me gustó la idea de forzar una recarga del sistema y no pude lograr que el sistema cooperara con la configuración de variables de entorno como PYTHONIOENCODING (probé la variable de entorno directa de Windows y también la dejé en un sitio personalizado.py en paquetes de sitio como uno solo) liner = 'utf-8').

Entonces, si está dispuesto a hackear su camino hacia el éxito, vaya a su directorio IDLE, típicamente: "C: \ Python27 \ Lib \ idlelib" Localice el archivo IOBinding.py. Haga una copia de ese archivo y guárdelo en otro lugar para que pueda volver al comportamiento original cuando lo desee. Abra el archivo en idlelib con un editor (por ejemplo, IDLE). Vaya a esta área de código:

# Encoding for file names
filesystemencoding = sys.getfilesystemencoding()

encoding = "ascii"
if sys.platform == 'win32':
    # On Windows, we could use "mbcs". However, to give the user
    # a portable encoding name, we need to find the code page 
    try:
        # --> 6/5/17 hack to force IDLE to display utf-8 rather than cp1252
        # --> encoding = locale.getdefaultlocale()[1]
        encoding = 'utf-8'
        codecs.lookup(encoding)
    except LookupError:
        pass

En otras palabras, comente la línea de código original después del ' intento ' que hacía que la variable de codificación fuera igual a locale.getdefaultlocale (porque eso le dará cp1252 que no desea) y, en su lugar, la fuerza bruta a 'utf-8 '(agregando la línea' encoding = 'utf-8 ' como se muestra).

Creo que esto solo afecta a la visualización IDLE en stdout y no a la codificación utilizada para los nombres de archivos, etc. (que se obtiene en el archivo anterior) Si tiene un problema con cualquier otro código que ejecute en IDLE más tarde, simplemente reemplace el archivo IOBinding.py con el archivo original no modificado.

Dalton Bentley
fuente
1

Puede cambiar la codificación de todo su sistema operativo. En Ubuntu puedes hacer esto con

sudo apt install locales 
sudo locale-gen en_US en_US.UTF-8    
sudo dpkg-reconfigure locales
Boris
fuente