Pregunta

Al canalizar la salida de un programa de Python, el intérprete de Python se confunde acerca de la codificación y lo 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 códec 'ascii' no puede codificar el carácter u '\ xa0' en la posición 0: el ordinal no está en el rango (128)

cuando se usa en una secuencia de tubería.

¿Cuál es la mejor manera de hacer que esto funcione al conectar tuberías? ¿Puedo decirle que use cualquier codificación del shell / sistema de archivos / lo que sea que esté usando?

Las sugerencias que he visto hasta ahora es modificar su site.py directamente, o codificar la codificación predeterminada utilizando 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?

¿Fue útil?

Solución

Su código funciona cuando se ejecuta en un script porque Python codifica la salida a la codificación que esté utilizando su aplicación de terminal. Si está canalizando, debe codificarlo usted mismo.

Una regla general 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 esté en mayúscula en el medio.

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, porque algunos módulos y bibliotecas que utiliza pueden confiar en el hecho de que es ASCII. No lo hagas.

Otros consejos

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 comienzo de su programa, para codificar con una codificación seleccionada. Aquí hay una solución que encontré en Python: ¿Cómo está sys? .stdout.encoding elegido? , 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 " a " utf_8 " ;. He escrito una página en mi terrible experiencia con este problema .

Tl; dr de la publicación del blog:

te da

utf_8
False
ANSI_X3.4-1968
ascii
utf_8
ö ☺ ☻
export PYTHONIOENCODING=utf-8

hace el trabajo, pero no puede configurarlo en Python ...

lo que podemos hacer es verificar si no está configurando y decirle al usuario que lo configure antes del script de llamada 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)

Actualización para responder al comentario: el problema solo existe cuando se conecta a stdout. Probé 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

ejecutando ./b.py | menos

None

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 ... - & Gt; Editor - & Gt; Codificación de archivos, luego establezca: & Quot; IDE Encoding & Quot ;, & Quot; Project Encoding & Quot; y " Codificación predeterminada para archivos de propiedades " TODO para UTF-8 y ahora funciona de maravilla.

¡Espero que esto ayude!

Una versión desinfectada 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'ÅÄÖåäö'

Podría " automatizar " 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 esto " setenv " falla.

Solo pensé en mencionar algo aquí con lo que tuve que pasar mucho tiempo experimentando antes de darme 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, así que en 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 "% " (AKA & Quot; operador de interpolación & Quot;) el mecanismo de construcción de cadenas también causa problemas ADICIONALES ... Si la codificación predeterminada del & Quot; ambiente & Quot; es ASCII e intentas hacer algo como

print( "bonjour, %s" % "fréd" )  # Call this "print A"

No tendrá dificultades para ejecutar en Eclipse ... En una CLI de Windows (ventana de DOS) encontrará que la codificación es página de códigos 850 (mi Windows & nbsp; 7 OS) o algo similar, que puede manejar caracteres con acento europeo al menos, por lo que funcionará.

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 stdout será Ninguna, que por defecto será ASCII (en mi SO de todos modos), que no podrá manejar ninguna de las impresiones anteriores ... (temido error de codificación).

Entonces, podría pensar en redirigir su stdout 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 anterior funcionará ... ¡Pero la impresión B anterior 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 Unicode cadena usando " u " el prefijo se envía al mecanismo de manejo de%, parece implicar el uso de la codificación de entorno predeterminada, ¡independientemente de si ha configurado stdout para redirigir!

La forma en que las personas lidian con esto es una cuestión de elección. Me gustaría que un experto de Unicode dijera por qué sucede esto, si me he equivocado de alguna manera, cuál es la solución preferida para esto, si también se aplica a CPython , ya sea que ocurra en Python 3, etc., etc.

En Ubuntu 12.10 y GNOME Terminal, no se produce ningún error cuando el programa está imprimiendo en stdout o conectado a una tubería para otros programas. Tanto la codificación de archivos como la codificación de terminal son 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 cuando usan iTerm & Nbsp; 2 y OS X; iTerm & nbsp; 2 puede ser el culpable.

Actualización: esta respuesta es incorrecta; vea los comentarios para obtener detalles

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)

Además 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'
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top