Domanda

Durante il piping dell'output di un programma Python, l'interprete Python viene confuso riguardo alla codifica e lo imposta su Nessuno. Ciò significa che un programma come questo:

# -*- coding: utf-8 -*-
print u"åäö"

funzionerà bene quando eseguito normalmente, ma non riesce con:

  

UnicodeEncodeError: il codec 'ascii' non può codificare il carattere u '\ xa0' in posizione 0: ordinale non compreso nell'intervallo (128)

se utilizzato in una sequenza di pipe.

Qual è il modo migliore per farlo funzionare durante il piping? Posso solo dirgli di usare qualunque codifica shell / filesystem / qualunque cosa stia usando?

I suggerimenti che ho visto finora è quello di modificare direttamente il tuo site.py, o hardcodificare la codifica predefinita usando questo hack:

# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
print u"åäö"

Esiste un modo migliore per far funzionare le tubazioni?

È stato utile?

Soluzione

Il codice funziona quando viene eseguito in uno script perché Python codifica l'output in qualunque codifica venga utilizzata dall'applicazione terminale. Se stai eseguendo il piping devi codificarlo tu stesso.

Una regola empirica è: utilizzare sempre Unicode internamente. Decodifica ciò che ricevi e codifica ciò che invii.

# -*- coding: utf-8 -*-
print u"åäö".encode('utf-8')

Un altro esempio didattico è un programma Python per convertire tra ISO-8859-1 e UTF-8, rendendo tutto tra maiuscole.

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)

L'impostazione della codifica predefinita del sistema è una cattiva idea, perché alcuni moduli e librerie che usi possono fare affidamento sul fatto che è ASCII. Non farlo.

Altri suggerimenti

Innanzitutto, riguardo a questa soluzione:

# -*- coding: utf-8 -*-
print u"åäö".encode('utf-8')

Non è pratico stampare esplicitamente con una determinata codifica ogni volta. Sarebbe ripetitivo e soggetto a errori.

Una soluzione migliore è cambiare sys.stdout all'inizio del programma, per codificare con una codifica selezionata. Ecco una soluzione che ho trovato su Python: How is sys .stdout.encoding scelto? , in particolare un commento di " toka " ;:

import sys
import codecs
sys.stdout = codecs.getwriter('utf8')(sys.stdout)

Potresti provare a cambiare la variabile di ambiente " PYTHONIOENCODING " a " utf_8 " ;. Ho scritto una sul mio calvario con questo problema .

Tl; dr del post del blog:

ti dà

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

esegue il lavoro, ma non può impostarlo su Python stesso ...

ciò che possiamo fare è verificare se non è impostato e dire all'utente di impostarlo prima di chiamare lo 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)

Aggiorna per rispondere al commento: il problema esiste solo quando si esegue il piping su stdout. Ho testato in Fedora 25 Python 2.7.13

python --version
Python 2.7.13

cat b.py

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

print sys.stdout.encoding

in esecuzione ./b.py

UTF-8

in esecuzione ./b.py | meno

None

Ho avuto un problema simile la scorsa settimana . È stato facile da risolvere nel mio IDE (PyCharm).

Ecco la mia soluzione:

A partire dalla barra dei menu di PyCharm: File - > Impostazioni ... - & Gt; Editor - & Gt; Codifiche file, quindi impostare: & Quot; Codifica IDE & Quot ;, & Quot; Codifica progetto & Quot; e " Codifica predefinita per i file delle proprietà " TUTTO su UTF-8 e ora funziona come un incantesimo.

Spero che questo aiuti!

Una discutibile versione disinfettata della risposta di 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

Utilizzo:

with EncodedOut('utf-8'):
    print u'ÅÄÖåäö'

Potrei " automatizzare " con una chiamata 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ì, è possibile ottenere un ciclo infinito qui se questo " setenv " fallisce.

Ho pensato di menzionare qui qualcosa che ho dovuto dedicare molto tempo a sperimentare prima di realizzare finalmente quello che stava succedendo. Questo può essere così ovvio per tutti qui che non si sono preoccupati di menzionarlo. Ma mi avrebbe aiutato se l'avessero fatto, quindi su quel principio ...!

NB: sto usando Jython in particolare, v 2.7, quindi forse questo potrebbe non essere applicabile a CPython ...

NB2: le prime due righe del mio file .py qui sono:

# -*- coding: utf-8 -*-
from __future__ import print_function

Il "% " (AKA & Quot; interpolazione operatore & Quot;) meccanismo di costruzione di stringhe causa anche problemi AGGIUNTIVI ... Se la codifica predefinita di & Quot; environment & Quot; è ASCII e provi a fare qualcosa del genere

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

Non avrai difficoltà a eseguire Eclipse ... In una CLI di Windows (finestra DOS) troverai che la codifica è code page 850 (il mio Windows & nbsp; 7 OS) o qualcosa di simile, in grado di gestire almeno i caratteri accentati europei, quindi funzionerà.

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

funzionerà anche.

Se, OTOH, sei diretto a un file dalla CLI, la codifica stdout sarà None, che per impostazione predefinita sarà ASCII (sul mio sistema operativo), che non sarà in grado di gestire nessuna delle stampe sopra ... (temuto errore di codifica).

Quindi potresti pensare di reindirizzare il tuo stdout usando

sys.stdout = codecs.getwriter('utf8')(sys.stdout)

e prova a eseguire il piping della CLI su un file ... Stranamente, la stampa A sopra funzionerà ... Ma la stampa B sopra genererà l'errore di codifica! Tuttavia, funzionerà correttamente:

print( u"bonjour, " + "fréd" ) # Call this "print C"

La conclusione a cui sono giunto (in via provvisoria) è che se una stringa che è specificata come Unicode stringa usando " u " il prefisso è sottoposto al meccanismo di gestione% che sembra comportare l'uso della codifica dell'ambiente predefinita, indipendentemente dal fatto che tu abbia impostato stdout per il reindirizzamento!

Il modo in cui le persone affrontano questo è una questione di scelta. Vorrei dare il benvenuto a un esperto Unicode per dire perché questo accade, se ho sbagliato in qualche modo, quale sia la soluzione preferita a questo, se si applica anche a CPython , indipendentemente dal fatto che accada in Python 3, ecc. ecc.

Su Ubuntu 12.10 e GNOME Terminal, non viene prodotto alcun errore quando il programma sta stampando su stdout o collegato a una pipe per altri programmi. Sia la codifica dei file che la codifica dei terminali sono UTF-8 .

$ cat a.py
# -*- coding: utf-8 -*-
print "åäö"
$ python a.py
åäö
$ python a.py | tee out
åäö

Quale sistema operativo ed emulatore di terminale stai usando? Ho sentito che alcuni dei miei colleghi hanno problemi simili usando iTerm & Nbsp; 2 e OS X; iTerm & nbsp; 2 potrebbe essere il colpevole.

Aggiornamento: questa risposta è errata. Consulta i commenti per i dettagli

Ho riscontrato questo problema in un'applicazione legacy ed è stato difficile identificare dove è stato stampato. Mi sono aiutato con questo trucco:

# 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)

In cima al mio script, test.py:

import encoding_utf8
string = 'Axwell Λ Ingrosso'
print(string)

Nota che questo cambia TUTTE le chiamate per stampare per usare una codifica, quindi la tua console stamperà questo:

$ python test.py
b'Axwell \xce\x9b Ingrosso'
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top