2010-12-07 12 views
35

Ajuste de la codificación de salida predeterminado en Python 2 es un lenguaje conocido:Cómo configurar la codificación sys.stdout en Python 3?

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

Esto ajusta el objeto sys.stdout en un escritor codec que codifica la producción en UTF-8.

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

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

+0

2to3 es una herramienta útil para preguntas como estas. –

+0

@dan_waterworth: No pensé en probar eso antes, pero intenté '2to3' ahora y no sugirió ningún cambio para el código dado. –

+3

Si el nuevo código no funciona, le sugiero que agregue esto como un error. –

Respuesta

30

Python 3.1 añade io.TextIOBase.detach(), con una nota en la documentación de sys.stdout:

Los flujos estándares 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 corrientes io.TextIOBase.detach() se puede hacer de forma binaria por defecto. Esta función establece stdin y stdout a binario:

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

Por lo tanto, el idioma correspondiente para Python 3.1 y más tarde es:

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

use 'PYTHONIOENCODING'; de lo contrario, 'io.TextIOWrapper' podría ser una alternativa mejor que' codecs' para manejar las nuevas líneas correctamente. – jfs

+0

Esto cambia totalmente el comportamiento de 'sys.stdout'. El 'StreamWriter' devuelto por' codecs.getwriter' ya no está en línea, por ejemplo – Sebastian

7

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

Cuando esto fracasarí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 igual de dandy en Python 3:

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

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

+2

Mi contexto estaba ejecutando el script de Python como un CGI en Apache, donde la codificación de salida predeterminada no era la que necesitaba (necesitaba UTF- 8). Creo que es mejor para el script asegurarse de que su salida está en la codificación correcta, en lugar de depender de configuraciones externas (como variables de entorno como PYTHONIOENCODING). –

+1

Otra prueba más de que el uso de stdout para la comunicación de procesos es un gran error. Me doy cuenta de que no tiene otra opción que usar CGI en este caso, así que no es su culpa. :-) –

+0

Si bien es cierto que 'sys.stdout' es un archivo * binario * en Python 2 y un archivo * text * en Python 3, creo que el ejemplo de Python 2 falla porque la cadena unicode' u "ûnicöde" ' que se codifica implícitamente en el método 'sys.stdout.write' tiene caracteres fuera del rango ASCII. Si cambia sus variables de entorno 'LC_CTYPE',' LANG' o 'PYTHONIOENCODING' a una codificación que tenga todos los caracteres en la cadena Unicode, no debería recibir ningún error. (Lo he probado en Python 2.7.) – Maggyero

16

Ajuste de la codificación de salida por defecto en Python 2 es un lenguaje conocido-

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

Sin duda arruinará cualquier script que intente escribir binario a stdout (lo 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 charset debe manejarse en un nivel superior en su aplicación (en los casos en los que devuelve contenido textual, en lugar de binario). Esto también significa que print ya no sirve para CGI.

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

+0

Este comentario debe actualizarse, con respecto a la versión 3.3 y al próximo lanzamiento de Python 3.4. Gracias – soshial

18

I encontrado este hilo, mientras que la búsqueda de soluciones para el mismo error,

Una solución alternativa a las ya sugerida es para establecer el entorno PYTHONIOENCODING variables antes Python comienza, para mi uso - esto es menos problemas para luego intercambiar sys.stdout después de Python es inicializado:

PYTHONIOENCODING=utf-8:surrogateescape python3 somescript.py 

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

+0

Thumbs-upping principalmente porque PYTHONIOENCODING = utf-8 resolvió mi problema, después de muchas horas de búsqueda. – theeggman85

5

Usando detach() hace que el intérprete de imprimir una advertencia cuando se 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 a default_out en lugar de stdout.)

15

Otras respuestas parecen recomendar el uso de codecs, pero open obras 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 corro con PYTHONIOENCODING="ascii".

Cuestiones relacionadas