python unicode python-3.x stdout

¿Cómo establecer la codificación sys.stdout en Python 3?



unicode python-3.x (6)

Establecer la codificación de salida predeterminada en Python 2 es una expresión idiomática bien conocida

¡Eek! ¿Es un idioma bien conocido en Python 2? Me parece un error peligroso.

Ciertamente estropeará cualquier script que intente escribir binario a stdout (que necesitará si es un script CGI que devuelve una imagen, por ejemplo). Bytes y caracteres son animales bastante diferentes; no es una buena idea montar una interfaz que se haya especificado para aceptar bytes con una que solo tome caracteres.

CGI y HTTP en general trabajan explícitamente con bytes. Solo debe enviar bytes a sys.stdout. En Python 3 eso significa usar sys.stdout.buffer.write para enviar bytes directamente. El contenido de la página de codificación para que coincida con su parámetro de juego de charset debe manejarse en un nivel más alto en su aplicación (en los casos en que devuelve contenido de texto en lugar de binario). Esto también significa que print ya no es bueno para CGI.

(Para aumentar la confusión, el CGIHandler de wsgiref se ha roto en py3k hasta hace muy poco, por lo que es imposible implementar WSGI en CGI de esa manera. Con PEP 3333 y Python 3.2 esto finalmente es factible).

Establecer la codificación de salida predeterminada en Python 2 es una expresión idiomática bien conocida:

sys.stdout = codecs.getwriter("utf-8")(sys.stdout)

Esto envuelve el objeto sys.stdout en una sys.stdout de códec que codifica la salida en UTF-8.

Sin embargo, esta técnica no funciona en Python 3 porque sys.stdout.write() espera un str , pero el resultado de la codificación es bytes , y se produce un error cuando los codecs intentan escribir los bytes codificados en el sys.stdout original.

¿Cuál es la forma correcta de hacer esto en Python 3?


El uso de detach() hace que el intérprete imprima una advertencia cuando intenta cerrar la salida estándar justo antes de que salga:

Exception ignored in: <_io.TextIOWrapper mode=''w'' encoding=''UTF-8''> ValueError: underlying buffer has been detached

En cambio, esto funcionó bien para mí:

default_out = io.TextIOWrapper(sys.stdout.buffer, encoding=''utf-8'')

(Y, por supuesto, escribiendo en default_out lugar de stdout).


Encontré este hilo mientras buscaba soluciones para el mismo error,

Una solución alternativa a las ya sugeridas es establecer la variable de entorno PYTHONIOENCODING antes de que inicie Python, para mi uso: esto es menos problemático que intercambiar sys.stdout después de inicializar Python:

PYTHONIOENCODING=utf-8:surrogateescape python3 somescript.py

Con la ventaja de no tener que ir y editar el código de Python.


Otras respuestas parecen recomendar el uso de codecs , pero open obras open para mí:

import sys sys.stdout = open(sys.stdout.fileno(), mode=''w'', encoding=''utf8'', buffering=1) print("日本語") # Also works with other methods of writing to stdout: sys.stdout.write("日本語/n") sys.stdout.buffer.write("日本語/n".encode())

Esto funciona incluso cuando lo ejecuto con PYTHONIOENCODING="ascii" .


Python 3.1 agregó io.TextIOBase.detach() , con una nota en la documentación para sys.stdout :

Las transmisiones estándar están en modo de texto por defecto. Para escribir o leer datos binarios a estos, use el búfer binario subyacente. Por ejemplo, para escribir bytes en stdout , use sys.stdout.buffer.write(b''abc'') . El uso de las io.TextIOBase.detach() se puede hacer de forma binaria por defecto. Esta función establece stdin y stdout en binario:

def make_streams_binary(): sys.stdin = sys.stdin.detach() sys.stdout = sys.stdout.detach()

Por lo tanto, la expresión correspondiente para Python 3.1 y posterior es:

sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())


sys.stdout está en modo texto en Python 3. Por lo tanto, usted escribe unicode directamente, y la expresión idiomática de Python 2 ya no es necesaria.

Donde esto fallaría en Python 2:

>>> import sys >>> sys.stdout.write(u"ûnicöde") Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeEncodeError: ''ascii'' codec can''t encode character u''/xfb'' in position 0: ordinal not in range(128)

Sin embargo, funciona muy bien en Python 3:

>>> import sys >>> sys.stdout.write("Ûnicöde") Ûnicöde7

Ahora bien, si su Python no sabe cuál es realmente su codificación de stdouts, ese es un problema diferente, muy probablemente en la versión de Python.