Configuración de la codificación correcta al canalizar la salida estándar en Python
encoding terminal (10)
En Ubuntu 12.10 y GNOME Terminal, no se produce ningún error cuando el programa se está imprimiendo en stdout o conectado a una tubería para otros programas. Tanto la codificación de archivo como la codificación de terminal es UTF-8 .
$ cat a.py
# -*- coding: utf-8 -*-
print "åäö"
$ python a.py
åäö
$ python a.py | tee out
åäö
¿Qué sistema operativo y emulador de terminal estás usando? Escuché que algunos de mis colegas tienen problemas similares al usar iTerm 2 y OS X; iTerm 2 puede ser el culpable.
Actualización: esta respuesta es incorrecta - ver comentarios para detalles
Al canalizar la salida de un programa Python, el intérprete de Python se confunde con respecto a la codificación y la establece en Ninguno. Esto significa un programa como este:
# -*- coding: utf-8 -*-
print u"åäö"
funcionará bien cuando se ejecuta normalmente, pero falla con:
UnicodeEncodeError: el codec ''ascii'' no puede codificar el carácter u ''/ xa0'' en la posición 0: ordinal no está dentro del rango (128)
cuando se utiliza en una secuencia de tubería.
¿Cuál es la mejor manera de hacer que esto funcione cuando se canaliza? ¿Puedo simplemente decirle que use cualquier codificación del shell / sistema de archivos / lo que esté usando?
Las sugerencias que he visto hasta ahora son modificar su site.py directamente, o codificar la codificación predeterminada usando este truco:
# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding(''utf-8'')
print u"åäö"
¿Hay una mejor manera de hacer que la tubería funcione?
Me encontré con este problema en una aplicación heredada y fue difícil identificar dónde se imprimió. Me ayudé con este truco:
# encoding_utf8.py
import codecs
import builtins
def print_utf8(text, **kwargs):
print(str(text).encode(''utf-8''), **kwargs)
def print_utf8(fn):
def print_fn(*args, **kwargs):
return fn(str(*args).encode(''utf-8''), **kwargs)
return print_fn
builtins.print = print_utf8(print)
En la parte superior de mi script, test.py:
import encoding_utf8
string = ''Axwell Λ Ingrosso''
print(string)
Tenga en cuenta que esto cambia TODAS las llamadas a imprimir para usar una codificación, por lo que su consola imprimirá esto:
$ python test.py
b''Axwell /xce/x9b Ingrosso''
Pensé que mencionaría algo aquí con lo que tuve que pasar mucho tiempo experimentando antes de que finalmente me diera cuenta de lo que estaba pasando. Esto puede ser tan obvio para todos aquí que no se han molestado en mencionarlo. ¡Pero me hubiera ayudado si lo hubieran hecho, entonces sobre ese principio ...!
NB: Estoy usando Jython específicamente, v 2.7, así que posiblemente esto no se aplique a CPython ...
NB2: las dos primeras líneas de mi archivo .py aquí son:
# -*- coding: utf-8 -*-
from __future__ import print_function
El mecanismo de construcción de cadena "%" (AKA "operador de interpolación") también causa problemas ADICIONALES ... Si la codificación predeterminada del "entorno" es ASCII e intenta hacer algo como
print( "bonjour, %s" % "fréd" ) # Call this "print A"
No tendrá dificultades para ejecutar Eclipse ... En una CLI de Windows (ventana de DOS) encontrará que la codificación es la página de códigos 850 (mi sistema operativo Windows 7) o algo similar, que puede manejar al menos caracteres con acento europeo, por lo que trabajaré
print( u"bonjour, %s" % "fréd" ) # Call this "print B"
También funcionará.
Si, OTOH, dirige a un archivo desde la CLI, la codificación de la salida estándar será Ninguna, que de forma predeterminada será ASCII (en mi sistema operativo), que no podrá manejar ninguna de las impresiones anteriores ... (codificación temida error).
Entonces, podría pensar en redireccionar su salida estándar usando
sys.stdout = codecs.getwriter(''utf8'')(sys.stdout)
e intente ejecutar en la tubería CLI a un archivo ... Muy extrañamente, la impresión A de arriba funcionará ... ¡Pero la impresión B de arriba arrojará el error de codificación! Sin embargo, lo siguiente funcionará bien:
print( u"bonjour, " + "fréd" ) # Call this "print C"
La conclusión a la que he llegado (provisionalmente) es que si una cadena que se especifica como una cadena Unicode usa el prefijo "u" se envía al mecanismo% -handling, parece implicar el uso de la codificación del entorno por defecto, independientemente de Si ha configurado la salida estándar para redireccionar!
La forma en que la gente trata esto es una cuestión de elección. Daría la bienvenida a un experto de Unicode para decir por qué sucede esto, si me he equivocado de alguna manera, cuál es la solución preferida a esto, si también se aplica a CPython , si sucede en Python 3, etc., etc.
Podría "automatizarlo" con una llamada a:
def __fix_io_encoding(last_resort_default=''UTF-8''):
import sys
if [x for x in (sys.stdin,sys.stdout,sys.stderr) if x.encoding is None] :
import os
defEnc = None
if defEnc is None :
try:
import locale
defEnc = locale.getpreferredencoding()
except: pass
if defEnc is None :
try: defEnc = sys.getfilesystemencoding()
except: pass
if defEnc is None :
try: defEnc = sys.stdin.encoding
except: pass
if defEnc is None :
defEnc = last_resort_default
os.environ[''PYTHONIOENCODING''] = os.environ.get("PYTHONIOENCODING",defEnc)
os.execvpe(sys.argv[0],sys.argv,os.environ)
__fix_io_encoding() ; del __fix_io_encoding
Sí, es posible obtener un bucle infinito aquí si este "setenv" falla.
Primero, con respecto a esta solución:
# -*- coding: utf-8 -*-
print u"åäö".encode(''utf-8'')
No es práctico imprimir explícitamente con una codificación determinada cada vez. Eso sería repetitivo y propenso a errores.
Una mejor solución es cambiar sys.stdout
al inicio de su programa, para codificar con una codificación seleccionada. Aquí hay una solución que encontré en Python: ¿Cómo se elige sys.stdout.encoding? , en particular un comentario de "toka":
import sys
import codecs
sys.stdout = codecs.getwriter(''utf8'')(sys.stdout)
Puede intentar cambiar la variable de entorno "PYTHONIOENCODING" por "utf_8". He escrito una página en mi prueba con este problema .
Tl; dr de la entrada del blog:
import sys, locale, os
print(sys.stdout.encoding)
print(sys.stdout.isatty())
print(locale.getpreferredencoding())
print(sys.getfilesystemencoding())
print(os.environ["PYTHONIOENCODING"])
print(chr(246), chr(9786), chr(9787))
te dio
utf_8
False
ANSI_X3.4-1968
ascii
utf_8
ö ☺ ☻
Su código funciona cuando se ejecuta en una secuencia de comandos porque Python codifica la salida a cualquier codificación que esté utilizando su aplicación de terminal. Si estás canalizando debes codificarlo tú mismo.
Una regla de oro es: Siempre use Unicode internamente. Decodifica lo que recibes y codifica lo que envías.
# -*- coding: utf-8 -*-
print u"åäö".encode(''utf-8'')
Otro ejemplo didáctico es un programa Python para convertir entre ISO-8859-1 y UTF-8, haciendo que todo entre mayúsculas y minúsculas.
import sys
for line in sys.stdin:
# Decode what you receive:
line = line.decode(''iso8859-1'')
# Work with Unicode internally:
line = line.upper()
# Encode what you send:
line = line.encode(''utf-8'')
sys.stdout.write(line)
Establecer la codificación predeterminada del sistema es una mala idea, ya que algunos módulos y bibliotecas que utiliza pueden confiar en el hecho de que es ASCII. No lo hagas
Tuve un problema similar la semana pasada . Fue fácil de arreglar en mi IDE (PyCharm).
Aquí estaba mi solución:
Comenzando desde la barra de menú de PyCharm: Archivo -> Configuración ... -> Editor -> Codificaciones de archivos, luego configure: "Codificación IDE", "Codificación del proyecto" y "Codificación predeterminada para archivos de propiedades" TODO a UTF-8 y ahora funciona como un encanto.
¡Espero que esto ayude!
Una versión desechable discutible de la respuesta de Craig McQueen.
import sys, codecs
class EncodedOut:
def __init__(self, enc):
self.enc = enc
self.stdout = sys.stdout
def __enter__(self):
if sys.stdout.encoding is None:
w = codecs.getwriter(self.enc)
sys.stdout = w(sys.stdout)
def __exit__(self, exc_ty, exc_val, tb):
sys.stdout = self.stdout
Uso:
with EncodedOut(''utf-8''):
print u''ÅÄÖåäö''
export PYTHONIOENCODING=utf-8
hacer el trabajo, pero no puede configurarlo en Python en sí ...
lo que podemos hacer es verificar si no está configurado y decirle al usuario que lo configure antes de llamar al script con:
if __name__ == ''__main__'':
if (sys.stdout.encoding is None):
print >> sys.stderr, "please set python env PYTHONIOENCODING=UTF-8, example: export PYTHONIOENCODING=UTF-8, when write to stdout."
exit(1)
Actualizar para responder al comentario: el problema simplemente existe cuando se canaliza a la salida estándar. He probado en Fedora 25 Python 2.7.13
python --version
Python 2.7.13
gato b.py
#!/usr/bin/env python
#-*- coding: utf-8 -*-
import sys
print sys.stdout.encoding
ejecutando ./b.py
UTF-8
corriendo ./b.py | Menos
None