Changer l'encodage par défaut de Python?

143

J'ai de nombreux problèmes "impossible d'encoder" et "impossible de décoder" avec Python lorsque j'exécute mes applications depuis la console. Mais dans l' IDE Eclipse PyDev , l'encodage de caractères par défaut est défini sur UTF-8 , et ça va.

J'ai cherché partout pour définir l'encodage par défaut, et les gens disent que Python supprime la sys.setdefaultencodingfonction au démarrage et que nous ne pouvons pas l'utiliser.

Alors, quelle est la meilleure solution pour cela?

Ali Nadalizadeh
la source
1
Voir le billet de blog The Illusive setdefaultencoding .
djc
3
The best solution is to learn to use encode and decode correctly instead of using hacks.Cela était certainement possible avec python2 au prix de toujours se souvenir de le faire / en utilisant systématiquement votre propre interface. Mon expérience suggère que cela devient très problématique lorsque vous écrivez du code que vous souhaitez utiliser à la fois avec python2 et python3.
Att Righ

Réponses:

159

Voici une méthode plus simple (hack) qui vous rend la setdefaultencoding()fonction qui a été supprimée de sys:

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

(Remarque pour Python 3.4+: se reload()trouve dans la importlibbibliothèque.)

Cependant, ce n'est pas une chose sûre à faire : il s'agit évidemment d'un hack, car il sys.setdefaultencoding()est volontairement supprimé du sysdémarrage de Python. Le réactiver et changer le codage par défaut peut casser le code qui repose sur ASCII comme valeur par défaut (ce code peut être tiers, ce qui rendrait généralement sa résolution impossible ou dangereuse).

Eric O Lebigot
la source
5
J'ai voté contre, parce que cette réponse n'aide pas pour exécuter des applications existantes (ce qui est une façon d'interpréter la question), est erronée lorsque vous écrivez / maintenez une application et dangereuse lors de l'écriture d'une bibliothèque. La bonne façon est de définir LC_CTYPE(ou dans une application, de vérifier si elle est définie correctement et d'abandonner avec un message d'erreur significatif).
ibotty
@ibotty Je suis d'accord que cette réponse est un hack et qu'il est dangereux de l'utiliser. Cela répond cependant à la question ("Changer le codage par défaut de Python?"). Avez-vous une référence sur l'effet de la variable d'environnement LC_CTYPE sur l'interpréteur Python?
Eric O Lebigot
eh bien, il n'a pas mentionné, c'est un hack au début. à part cela, les réponses dangereuses qui ne mentionnent pas qu'elles le sont ne sont pas utiles.
ibotty
1
@EOL vous avez raison. Cela LC_CTYPE=C python -c 'import locale; print( locale.getpreferredencoding())'
affecte
1
@ user2394901 L'utilisation de sys.setdefaultencoding () a toujours été déconseillée !! Et le codage de py3k est câblé à "utf-8" et le changer déclenche une erreur.
Marlon Abeykoon
70

Si vous obtenez cette erreur lorsque vous essayez de diriger / rediriger la sortie de votre script

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

Exportez simplement PYTHONIOENCODING dans la console, puis exécutez votre code.

export PYTHONIOENCODING=utf8

Iman
la source
3
C'est la seule solution qui a fait une différence pour moi. - Je suis sur Debian 7, avec des paramètres régionaux défectueux. Merci.
Pryo
4
Réglez LC_CTYPEplutôt quelque chose de sensé. Cela fait également plaisir à tous les autres programmes.
ibotty
5
Un bogue plus important dans Python3 est que ce PYTHONIOENCODING=utf8n'est pas la valeur par défaut. Cela rend les scripts cassés juste parce queLC_ALL=C
Tino
Set LC_CTYPE to something sensible insteadC'est une suggestion raisonnable. Cela ne fonctionne pas très bien lorsque vous essayez de distribuer du code qui fonctionne uniquement sur le système d'une autre personne.
Att Righ
Les systèmes d'exploitation Debian et Redhat utilisent une C.utf8locale pour fournir des paramètres régionaux plus intelligents. C. glibc en amont travaille à l'ajouter, alors peut-être ne devrions-nous pas blâmer Python pour le respect des paramètres régionaux \…?
Arthur2e5
52

A) Pour contrôler la sys.getdefaultencoding()sortie:

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

ascii

ensuite

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

et

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

utf-16-be

Vous pouvez placer votre sitecustomize.py plus haut dans votre PYTHONPATH.

Vous aimerez peut-être aussi essayer reload(sys).setdefaultencodingpar @EOL

B) Pour contrôler stdin.encodinget que stdout.encodingvous souhaitez définir PYTHONIOENCODING:

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

ascii ascii

ensuite

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

utf-16-be utf-16-be

Enfin: vous pouvez utiliser A) ou B) ou les deux!

lukmdo
la source
(python2 uniquement) séparé mais intéressant s'étend ci-dessus avec from __future__ import unicode_literalsvoir la discussion
lukmdo
17

A partir de PyDev 3.4.1, l'encodage par défaut n'est pas changé plus. Voir ce ticket pour plus de détails.

Pour les versions antérieures, une solution consiste à s'assurer que PyDev ne fonctionne pas avec UTF-8 comme codage par défaut. Sous Eclipse, exécutez les paramètres de la boîte de dialogue ("exécuter les configurations", si je me souviens bien); vous pouvez choisir l'encodage par défaut dans l'onglet commun. Changez-le en US-ASCII si vous voulez avoir ces erreurs «tôt» (en d'autres termes: dans votre environnement PyDev). Consultez également un article de blog original pour cette solution de contournement .

ChristopheD
la source
1
Merci Chris. Surtout compte tenu du commentaire de Mark T ci-dessus, votre réponse me semble la plus appropriée. Et pour quelqu'un qui n'est pas principalement un utilisateur d'Eclipse / PyDev, je n'aurais jamais compris cela par moi-même.
Sean
J'aimerais changer cela globalement (plutôt qu'une fois par configuration d'exécution), mais je n'ai pas compris comment - j'ai demandé un autre q: stackoverflow.com/questions/9394277/...
Tim Diggins
13

En ce qui concerne python2 (et python2 uniquement), certaines des premières réponses reposent sur l'utilisation du hack suivant:

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

Il est déconseillé de l'utiliser (cochez ceci ou cela )

Dans mon cas, cela a un effet secondaire: j'utilise des notebooks ipython, et une fois que j'exécute le code, la fonction «print» ne fonctionne plus. Je suppose qu'il y aurait une solution à cela, mais je pense toujours que l'utilisation du hack ne devrait pas être la bonne option.

Après avoir essayé de nombreuses options, celle qui a fonctionné pour moi utilisait le même code dans le sitecustomize.py, où ce morceau de code est censé être . Après avoir évalué ce module, la fonction setdefaultencoding est supprimée de sys.

La solution est donc d'ajouter au fichier /usr/lib/python2.7/sitecustomize.pyle code:

import sys
sys.setdefaultencoding('UTF8')

Lorsque j'utilise virtualenvwrapper, le fichier que j'édite est ~/.virtualenvs/venv-name/lib/python2.7/sitecustomize.py.

Et quand j'utilise avec des notebooks python et conda, c'est ~/anaconda2/lib/python2.7/sitecustomize.py

Kiril
la source
8

Il y a un article de blog perspicace à ce sujet.

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

Je paraphrase son contenu ci-dessous.

En python 2, qui n'était pas aussi fortement typé en ce qui concerne le codage des chaînes, vous pouviez effectuer des opérations sur des chaînes codées différemment et réussir. Par exemple, ce qui suit reviendrait True.

u'Toshio' == 'Toshio'

Cela serait valable pour chaque chaîne (normale, sans préfixe) qui a été encodée sys.getdefaultencoding(), qui est par défaut ascii, mais pas les autres.

L'encodage par défaut était censé être modifié à l'échelle du système site.py, mais pas ailleurs. Les hacks (également présentés ici) pour le définir dans les modules utilisateur n'étaient que cela: des hacks, pas la solution.

Python 3 a changé le codage système par défaut en utf-8 (lorsque LC_CTYPE est compatible avec unicode), mais le problème fondamental a été résolu avec l'exigence d'encoder explicitement les chaînes "octets" chaque fois qu'elles sont utilisées avec des chaînes unicode.

ibotty
la source
4

Premièrement: reload(sys)et définir un encodage aléatoire par défaut uniquement en ce qui concerne la nécessité d'un flux de terminal de sortie est une mauvaise pratique. reloadchange souvent les choses dans sys qui ont été mises en place en fonction de l'environnement - par exemple les flux sys.stdin / stdout, sys.excepthook, etc.

Résolution du problème d'encodage sur stdout

La meilleure solution que je connaisse pour résoudre le problème de codage des printchaînes unicode et au-delà de l'ascii str(par exemple à partir de littéraux) sur sys.stdout est: prendre soin d'un sys.stdout (objet de type fichier) qui est capable et éventuellement tolérant vis-à-vis des besoins:

  • Quand sys.stdout.encodingest Nonepour une raison quelconque, ou inexistant, ou erronément faux ou "moins" que ce dont le terminal ou le flux stdout est réellement capable, alors essayez de fournir un .encodingattribut correct . Enfin en le remplaçant sys.stdout & sys.stderrpar un objet de type fichier à traduire.

  • Lorsque le terminal / flux ne peut toujours pas encoder tous les caractères Unicode, et que vous ne voulez pas les casser printjuste à cause de cela, vous pouvez introduire un comportement d'encodage avec remplacement dans l'objet de type fichier à traduire.

Voici un exemple:

#!/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()

Utilisation de littéraux de chaîne simples Beyond-ASCII dans le code Python 2/2 + 3

Je pense que la seule bonne raison de changer le codage par défaut global (en UTF-8 uniquement) concerne une décision de code source d' application - et non à cause de problèmes d'encodage de flux d'E / S: pour écrire des littéraux de chaîne au-delà de l'ascii dans le code sans être forcé pour toujours utiliser l' u'string'échappement de style unicode. Cela peut être fait de manière assez cohérente (malgré ce que dit l'article d' anonbadger ) en prenant soin d'une base de code source Python 2 ou Python 2 + 3 qui utilise de manière cohérente les littéraux de chaîne simple ascii ou UTF-8 - dans la mesure où ces chaînes sont potentiellement silencieuses. conversion unicode et se déplacer entre les modules ou potentiellement aller à stdout. Pour cela, préférez "# encoding: utf-8"ou ascii (pas de déclaration). Changez ou supprimez des bibliothèques qui reposent encore de manière très stupide sur les erreurs de codage par défaut ascii au-delà de chr # 127 (ce qui est rare aujourd'hui).

Et faites comme ceci au démarrage de l'application (et / ou via sitecustomize.py) en plus du SmartStdoutschéma ci-dessus - sans utiliser 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

De cette façon, les littéraux de chaîne et la plupart des opérations (à l'exception de l'itération de caractères) fonctionnent à l'aise sans penser à la conversion Unicode comme s'il n'y aurait que Python3. Les E / S de fichiers nécessitent bien sûr toujours une attention particulière concernant les encodages - comme c'est le cas en Python3.

Remarque: les chaînes plains sont ensuite implicitement converties de utf-8 en unicode SmartStdoutavant d'être converties en flux de sortie enconding.

kxr
la source
4

Voici l'approche que j'ai utilisée pour produire du code compatible avec python2 et python3 et qui produisait toujours une sortie utf8 . J'ai trouvé cette réponse ailleurs, mais je ne me souviens plus de la source.

Cette approche fonctionne en remplaçant sys.stdoutpar quelque chose qui ne ressemble pas tout à fait à un fichier (mais qui n'utilise toujours que des éléments de la bibliothèque standard). Cela peut bien causer des problèmes pour vos bibliothèques sous-jacentes, mais dans le cas simple où vous avez un bon contrôle sur la façon dont sys.stdout out est utilisé dans votre framework, cela peut être une approche raisonnable.

sys.stdout = io.open(sys.stdout.fileno(), 'w', encoding='utf8')
Att Righ
la source
3

Cela a résolu le problème pour moi.

import os
os.environ["PYTHONIOENCODING"] = "utf-8"
Twasbrillig
la source
1

C'est un hack rapide pour quiconque est (1) sur une plate-forme Windows (2) exécutant Python 2.7 et (3) ennuyé parce qu'un bon logiciel (c'est-à-dire, pas écrit par vous donc pas immédiatement un candidat pour encoder / décoder l'impression manœuvres) n'afficheront pas les «jolis caractères unicode» dans l'environnement IDLE (Pythonwin imprime unicode finement), par exemple, les symboles de logique du premier ordre que Stephan Boyer utilise dans la sortie de son prouveur pédagogique au premier ordre logique prouveur .

Je n'aimais pas l'idée de forcer un rechargement système et je ne pouvais pas faire en sorte que le système coopère avec la définition de variables d'environnement telles que PYTHONIOENCODING (essayé la variable d'environnement Windows directe et la déposer dans un sitecustomize.py dans des packages de site comme un liner = 'utf-8').

Donc, si vous êtes prêt à pirater votre chemin vers le succès, allez dans votre répertoire IDLE, généralement: "C: \ Python27 \ Lib \ idlelib" Localisez le fichier IOBinding.py. Faites une copie de ce fichier et stockez-le ailleurs afin de pouvoir revenir au comportement d'origine lorsque vous le souhaitez. Ouvrez le fichier dans idlelib avec un éditeur (par exemple, IDLE). Accédez à cette zone de code:

# 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

En d'autres termes, commentez la ligne de code d'origine après le ' try ' qui rendait la variable d'encodage égale à locale.getdefaultlocale (car cela vous donnera cp1252 dont vous ne voulez pas) et à la place, forcez-le brutalement à 'utf-8 '(en ajoutant la ligne' encoding = 'utf-8 ' comme indiqué).

Je crois que cela n'affecte que l'affichage IDLE sur stdout et non l'encodage utilisé pour les noms de fichiers, etc. (qui est obtenu dans le codage du système de fichiers antérieur). Si vous rencontrez un problème avec un autre code que vous exécutez dans IDLE ultérieurement, remplacez simplement le fichier IOBinding.py par le fichier d'origine non modifié.

Dalton Bentley
la source
1

Vous pouvez modifier l'encodage de l'ensemble de votre système d'exploitation. Sur Ubuntu, vous pouvez le faire avec

sudo apt install locales 
sudo locale-gen en_US en_US.UTF-8    
sudo dpkg-reconfigure locales
Boris
la source