Modifica della codifica predefinita di Python?


143

Ho molti problemi "impossibile codificare" e "impossibile decodificare" con Python quando eseguo le mie applicazioni dalla console. Ma nell'IDE Eclipse PyDev , la codifica dei caratteri predefinita è impostata su UTF-8 , e sto bene.

Ho cercato in giro per impostare la codifica predefinita e la gente dice che Python cancella la sys.setdefaultencodingfunzione all'avvio e non possiamo usarla.

Quindi qual è la soluzione migliore per questo?


1
Vedi il post sul blog The Illusive setdefaultencoding .
DJ

3
The best solution is to learn to use encode and decode correctly instead of using hacks.Questo è stato certamente possibile con python2 al costo di ricordare sempre di farlo / usando costantemente la propria interfaccia. La mia esperienza suggerisce che questo diventa altamente problematico quando si scrive codice che si desidera lavorare con python2 e python3.
Att Righ,

Risposte:


159

Ecco un metodo più semplice (hack) che ti restituisce la setdefaultencoding()funzione che è stata eliminata da sys:

import sys
# sys.setdefaultencoding() does not exist, here!
reload(sys)  # Reload does the trick!
sys.setdefaultencoding('UTF8')

(Nota per Python 3.4+: reload()è nella importliblibreria.)

Questa non è una cosa sicura da fare , però: si tratta ovviamente di un hack, poiché sys.setdefaultencoding()viene volutamente rimosso da sysquando Python viene avviato. Riattivarlo e modificare la codifica predefinita può interrompere il codice che si basa sul fatto che ASCII è il valore predefinito (questo codice può essere di terze parti, il che generalmente renderebbe impossibile o pericoloso correggerlo).


5
Ho effettuato il downgrade, perché quella risposta non aiuta a eseguire applicazioni esistenti (che è un modo per interpretare la domanda), è errata quando si scrive / mantiene un'applicazione e pericolosa quando si scrive una libreria. Il modo giusto è impostare LC_CTYPE(o in un'applicazione, verificare se è impostato correttamente e interrompere con un messaggio di errore significativo).
ibotty,

@ibotty Sono d'accordo sul fatto che questa risposta sia un hack e che sia pericoloso usarla. Risponde alla domanda, però ("Cambiare la codifica predefinita di Python?"). Hai un riferimento sull'effetto della variabile d'ambiente LC_CTYPE sull'interprete Python?
Eric O Lebigot il

beh, non ha menzionato, all'inizio è un trucco. a parte questo, le risposte pericolose che non hanno alcuna menzione del fatto che non sono utili.
ibotty,

1
@EOL hai ragione. Ha effetto sulla LC_CTYPE=C python -c 'import locale; print( locale.getpreferredencoding())'
codifica

1
@ user2394901 L'uso di sys.setdefaultencoding () è sempre stato scoraggiato !! E la codifica di py3k è cablata su "utf-8" e la sua modifica genera un errore.
Marlon Abeykoon,

70

Se viene visualizzato questo errore quando si tenta di reindirizzare / reindirizzare l'output dello script

UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

Basta esportare PYTHONIOENCODING in console e quindi eseguire il codice.

export PYTHONIOENCODING=utf8


3
Questa è l'unica soluzione che ha fatto la differenza per me. - Sono su Debian 7, con impostazioni locali non corrette. Grazie.
Pryo,

4
Imposta LC_CTYPEinvece qualcosa di sensato. Rende felici anche tutti gli altri programmi.
ibotty,

5
Un bug più grande in Python3 è che PYTHONIOENCODING=utf8non è quello predefinito. Questo fa sì che gli script si rompano solo perchéLC_ALL=C
Tino,

Set LC_CTYPE to something sensible insteadQuesto è un suggerimento ragionevole. Questo non funziona così bene quando stai cercando di distribuire codice che funziona solo sul sistema di un'altra persona.
Att Righ,

I sistemi operativi Debian e Redhat usano una C.utf8localizzazione per fornire più sensibile C. glibc upstream sta lavorando per aggiungerla, quindi forse non dovremmo incolpare Python per il rispetto delle impostazioni locali \ ...?
Arthur2e5,

52

A) Per controllare l' sys.getdefaultencoding()uscita:

python -c 'import sys; print(sys.getdefaultencoding())'

ascii

Poi

echo "import sys; sys.setdefaultencoding('utf-16-be')" > sitecustomize.py

e

PYTHONPATH=".:$PYTHONPATH" python -c 'import sys; print(sys.getdefaultencoding())'

utf-16-be

Potresti mettere il tuo sitecustomize.py più in alto nel tuo PYTHONPATH.

Inoltre potresti provare reload(sys).setdefaultencodingcon @EOL

B) Per controllare stdin.encodinge stdout.encodingsi desidera impostare PYTHONIOENCODING:

python -c 'import sys; print(sys.stdin.encoding, sys.stdout.encoding)'

ascii ascii

Poi

PYTHONIOENCODING="utf-16-be" python -c 'import sys; 
print(sys.stdin.encoding, sys.stdout.encoding)'

utf-16-be utf-16-be

Finalmente: puoi usare A) o B) o entrambi!


(solo python2) separato ma interessante si sta estendendo sopra con from __future__ import unicode_literalsvedi discussione
lukmdo

17

A partire da PyDev 3.4.1, la codifica predefinita non viene più modificata. Vedi questo biglietto per i dettagli.

Per le versioni precedenti, una soluzione è assicurarsi che PyDev non venga eseguito con UTF-8 come codifica predefinita. Sotto Eclipse, esegui le impostazioni della finestra di dialogo ("esegui configurazioni", se ricordo bene); puoi scegliere la codifica predefinita nella scheda comune. Modificalo in US-ASCII se vuoi avere questi errori "in anticipo" (in altre parole: nel tuo ambiente PyDev). Vedi anche un post sul blog originale per questa soluzione alternativa .


1
Grazie Chris. Soprattutto considerando il commento di Mark T sopra, la tua risposta sembra essere la più appropriata per me. E per qualcuno che non è principalmente un utente Eclipse / PyDev, non avrei mai capito da solo.
Sean,

Vorrei cambiarlo a livello globale (piuttosto che una volta per configurazione di esecuzione), ma non ho capito come - ho chiesto un q separato: stackoverflow.com/questions/9394277/…
Tim Diggins

13

Per quanto riguarda python2 (e solo python2), alcune delle risposte precedenti si basano sull'uso del seguente hack:

import sys
reload(sys)  # Reload is a hack
sys.setdefaultencoding('UTF8')

Si sconsiglia di usarlo (controllare questo o questo )

Nel mio caso, ha un effetto collaterale: sto usando i notebook ipython e una volta eseguito il codice la funzione "impronta" non funziona più. Immagino che ci sarebbe una soluzione, ma penso comunque che usare l'hack non dovrebbe essere l'opzione corretta.

Dopo aver provato molte opzioni, quella che ha funzionato per me stava usando lo stesso codice in sitecustomize.py, dove quel pezzo di codice doveva essere . Dopo aver valutato quel modulo, la funzione setdefaultencoding viene rimossa da sys.

Quindi la soluzione è aggiungere al file /usr/lib/python2.7/sitecustomize.pyil codice:

import sys
sys.setdefaultencoding('UTF8')

Quando uso virtualenvwrapper il file che modifico è ~/.virtualenvs/venv-name/lib/python2.7/sitecustomize.py.

E quando lo uso con i notebook e conda Python, lo è ~/anaconda2/lib/python2.7/sitecustomize.py


8

C'è un post sul blog penetrante a riguardo.

Vedi https://anonbadger.wordpress.com/2015/06/16/why-sys-setdefaultencoding-will-break-code/ .

Parafrasando il suo contenuto di seguito.

In Python 2, che non era così fortemente tipizzato per quanto riguarda la codifica delle stringhe, è possibile eseguire operazioni su stringhe con codifica diversa e avere esito positivo. Ad esempio, sarebbe tornato quanto segue True.

u'Toshio' == 'Toshio'

Ciò rimarrebbe valido per ogni stringa (normale, non prefissata) che è stata codificata sys.getdefaultencoding(), che per impostazione predefinita era ascii, ma non per altre.

La codifica predefinita doveva essere modificata a livello di sistema site.py, ma non altrove. Gli hack (presentati anche qui) per inserirlo nei moduli utente erano proprio questo: hack, non la soluzione.

Python 3 ha modificato la codifica di sistema in default in utf-8 (quando LC_CTYPE è unicode-compatibile), ma il problema fondamentale è stato risolto con il requisito di codificare esplicitamente le stringhe "byte" ogni volta che vengono utilizzate con stringhe unicode.


4

Primo: reload(sys)impostare una codifica predefinita casuale solo per quanto riguarda la necessità di un flusso di terminali di output è una cattiva pratica. reloadcambia spesso le cose in sys che sono state messe in atto in base all'ambiente - ad esempio flussi sys.stdin / stdout, sys.excepthook, ecc.

Risolvere il problema di codifica su stdout

La migliore soluzione che conosco per risolvere il problema di codifica di print'ing stringhe unicode e oltre-ascii str' (es. Letterali) su sys.stdout è: prendersi cura di un sys.stdout (oggetto simile a un file) che è capace e facoltativamente tollerante rispetto alle esigenze:

  • Quando sys.stdout.encodingè Noneper qualche motivo, o inesistente, o erroneamente falso o "meno" di quello di cui è realmente capace il terminale stdout o lo stream, allora prova a fornire un .encodingattributo corretto . Finalmente sostituendo sys.stdout & sys.stderrcon un oggetto simile a file di traduzione.

  • Quando il terminale / flusso non è ancora in grado di codificare tutti i caratteri Unicode che si verificano e quando non si desidera interrompere printsolo per questo motivo, è possibile introdurre un comportamento di codifica con sostituzione nell'oggetto simile a file di traduzione.

Ecco un esempio:

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

class SmartStdout:
    def __init__(self, encoding=None, org_stdout=None):
        if org_stdout is None:
            org_stdout = getattr(sys.stdout, 'org_stdout', sys.stdout)
        self.org_stdout = org_stdout
        self.encoding = encoding or \
                        getattr(org_stdout, 'encoding', None) or 'utf-8'
    def write(self, s):
        self.org_stdout.write(s.encode(self.encoding, 'backslashreplace'))
    def __getattr__(self, name):
        return getattr(self.org_stdout, name)

if __name__ == '__main__':
    if sys.stdout.isatty():
        sys.stdout = sys.stderr = SmartStdout()

    us = u'aouäöüфżß²'
    print us
    sys.stdout.flush()

Utilizzo dei valori letterali stringa stringa beyond-ascii nel codice Python 2/2 + 3

L'unico buon motivo per cambiare la codifica globale predefinita (solo su UTF-8) penso sia riguardo a una decisione sul codice sorgente dell'applicazione - e non a causa di problemi di codifica del flusso I / O: per scrivere valori letterali stringa oltre-ascii nel codice senza essere forzati per usare sempre lo u'string'stile Unicode Escaping. Questo può essere fatto in modo piuttosto coerente (nonostante quanto dice l'articolo di Anonbadger ) prendendosi cura di una base di codice sorgente Python 2 o Python 2 + 3 che usa coerentemente letterali di stringhe ASCII o UTF-8 coerentemente, per quanto quelle stringhe potenzialmente subiscano silenziose conversione unicode e spostamento tra i moduli o potenzialmente passaggio a stdout. Per questo, preferisci "# encoding: utf-8"o ascii (nessuna dichiarazione). Cambia o elimina le librerie che si basano ancora fatalmente sugli errori di codifica ascii di default oltre chr # 127 (che è raro oggi).

E fai così all'avvio dell'applicazione (e / o tramite sitecustomize.py) oltre allo SmartStdoutschema sopra - senza usare reload(sys):

...
def set_defaultencoding_globally(encoding='utf-8'):
    assert sys.getdefaultencoding() in ('ascii', 'mbcs', encoding)
    import imp
    _sys_org = imp.load_dynamic('_sys_org', 'sys')
    _sys_org.setdefaultencoding(encoding)

if __name__ == '__main__':
    sys.stdout = sys.stderr = SmartStdout()
    set_defaultencoding_globally('utf-8') 
    s = 'aouäöüфżß²'
    print s

In questo modo i letterali di stringa e la maggior parte delle operazioni (tranne l'iterazione dei caratteri) funzionano in modo confortevole senza pensare alla conversione unicode come se ci fosse solo Python3. Naturalmente, l'I / O dei file richiede sempre particolare attenzione per quanto riguarda le codifiche, come in Python3.

Nota: le stringhe di pianura vengono quindi convertite implicitamente da utf-8 in unicode SmartStdoutprima di essere convertite nel flusso di output che lo circonda.


4

Ecco l'approccio che ho usato per produrre codice compatibile con python2 e python3 e che ha sempre prodotto output utf8 . Ho trovato questa risposta altrove, ma non ricordo la fonte.

Questo approccio funziona sostituendo sys.stdoutcon qualcosa che non è del tutto simile a un file (ma utilizza ancora solo cose nella libreria standard). Ciò può causare problemi per le librerie sottostanti, ma nel semplice caso in cui si ha un buon controllo su come viene utilizzato sys.stdout out nel proprio framework, questo può essere un approccio ragionevole.

sys.stdout = io.open(sys.stdout.fileno(), 'w', encoding='utf8')

3

Ciò ha risolto il problema per me.

import os
os.environ["PYTHONIOENCODING"] = "utf-8"

1

Questo è un trucco rapido per chiunque sia (1) su una piattaforma Windows (2) con Python 2.7 e (3) infastidito perché un bel software (cioè non scritto da te, quindi non immediatamente candidato alla stampa di codifica / decodifica manovre) non visualizzerà i "caratteri piuttosto unicode" nell'ambiente IDLE (Pythonwin stampa bene unicode), ad esempio, i simboli logici del Primo Ordine accurati che Stephan Boyer utilizza nell'output dal suo proverbio pedagogico al Prover Logico Primo Ordine .

Non mi piaceva l'idea di forzare un ricaricamento di sistema e non riuscivo a far cooperare il sistema con l'impostazione di variabili di ambiente come PYTHONIOENCODING (ho provato a modificare direttamente la variabile di ambiente Windows e a rilasciarla in un sitecustomize.py nei pacchetti del sito come uno liner = 'utf-8').

Quindi, se sei disposto ad hackerare la tua strada verso il successo, vai alla tua directory IDLE, in genere: "C: \ Python27 \ Lib \ idlelib" Individua il file IOBinding.py. Crea una copia di quel file e memorizzalo altrove in modo da poter ripristinare il comportamento originale quando lo desideri. Apri il file in idlelib con un editor (ad es. IDLE). Vai a questa area di codice:

# Encoding for file names
filesystemencoding = sys.getfilesystemencoding()

encoding = "ascii"
if sys.platform == 'win32':
    # On Windows, we could use "mbcs". However, to give the user
    # a portable encoding name, we need to find the code page 
    try:
        # --> 6/5/17 hack to force IDLE to display utf-8 rather than cp1252
        # --> encoding = locale.getdefaultlocale()[1]
        encoding = 'utf-8'
        codecs.lookup(encoding)
    except LookupError:
        pass

In altre parole, commenta la riga di codice originale seguendo il ' try ' che stava rendendo la variabile di codifica uguale a locale.getdefaultlocale (perché ciò ti darà cp1252 che non vuoi) e invece forzalo brutalmente su 'utf-8 '(aggiungendo la riga' encoding = 'utf-8 ' come mostrato).

Credo che ciò influisca solo sulla visualizzazione IDLE su stdout e non sulla codifica utilizzata per i nomi dei file, ecc. (Che si ottiene nella codifica del filesystem prima). In caso di problemi con qualsiasi altro codice eseguito in IDLE in un secondo momento, è sufficiente sostituire il file IOBinding.py con il file originale non modificato.


1

È possibile modificare la codifica dell'intero sistema operativo. Su Ubuntu puoi farlo con

sudo apt install locales 
sudo locale-gen en_US en_US.UTF-8    
sudo dpkg-reconfigure locales
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.