Définir l'encodage de sortie par défaut dans Python 2 est un idiome bien connu:
sys.stdout = codecs.getwriter("utf-8")(sys.stdout)
Cela encapsule l'objet sys.stdout
Dans un écrivain de codec qui code la sortie en UTF-8.
Cependant, cette technique ne fonctionne pas dans Python 3 car sys.stdout.write()
attend un str
, mais le résultat de l'encodage est bytes
, et une erreur se produit lorsque codecs
essaie d'écrire les octets codés dans le sys.stdout
d'origine.
Quelle est la bonne façon de le faire dans Python 3?
Depuis Python 3.7 vous pouvez changer l'encodage des flux standard avec reconfigure()
:
sys.stdout.reconfigure(encoding='utf-8')
Vous pouvez également modifier la façon dont les erreurs de codage sont gérées en ajoutant un paramètre errors
.
Python 3.1 a ajouté io.TextIOBase.detach()
, avec une note dans la documentation pour sys.stdout
:
Les flux standard sont en mode texte par défaut. Pour y écrire ou lire des données binaires, utilisez le tampon binaire sous-jacent. Par exemple, pour écrire des octets dans
stdout
, utilisezsys.stdout.buffer.write(b'abc')
. L'utilisation deio.TextIOBase.detach()
streams peut être rendue binaire par défaut. Cette fonction définitstdin
etstdout
sur binaire:def make_streams_binary(): sys.stdin = sys.stdin.detach() sys.stdout = sys.stdout.detach()
Par conséquent, l'idiome correspondant pour Python 3.1 et versions ultérieures est:
sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())
J'ai trouvé ce fil en recherchant des solutions à la même erreur,
Une solution alternative à celles déjà suggérées consiste à définir la variable d'environnement PYTHONIOENCODING
avant Python démarre, pour mon usage - cela pose moins de problèmes que l'échange sys.stdout
après Python est initialisé:
PYTHONIOENCODING=utf-8:surrogateescape python3 somescript.py
Avec l'avantage de ne pas avoir à aller éditer le code Python.
D'autres réponses semblent recommander l'utilisation de codecs
, mais open
fonctionne pour moi:
import sys
sys.stdout = open(sys.stdout.fileno(), mode='w', encoding='utf8', buffering=1)
print("日本語")
# Also works with other methods of writing to stdout:
sys.stdout.write("日本語\n")
sys.stdout.buffer.write("日本語\n".encode())
Cela fonctionne même lorsque je l'exécute avec PYTHONIOENCODING="ascii"
.
Définir l'encodage de sortie par défaut dans Python 2 est un idiome bien connu
Eek! Est-ce un idiome bien connu dans Python 2? Cela me semble une erreur dangereuse.
Cela gâchera certainement tout script qui essaie d'écrire du binaire sur stdout (dont vous aurez besoin si vous êtes un script CGI renvoyant une image, par exemple). Les octets et les caractères sont des animaux très différents; ce n'est pas une bonne idée de patcher une interface qui est spécifiée pour accepter des octets avec une qui ne prend que des caractères.
CGI et HTTP en général fonctionnent explicitement avec des octets. Vous ne devez envoyer des octets qu'à sys.stdout. Dans Python 3 cela signifie utiliser sys.stdout.buffer.write
pour envoyer des octets directement. L'encodage du contenu de la page pour correspondre à son paramètre charset
doit être géré à un niveau supérieur dans votre application (dans les cas où vous renvoyez du contenu textuel plutôt que binaire). Cela signifie également que print
n'est plus bon pour CGI.
(Pour ajouter à la confusion, le CGIHandler de wsgiref a été cassé dans py3k jusqu'à très récemment, ce qui rend impossible le déploiement de WSGI sur CGI de cette façon. Avec PEP 3333 et Python 3.2, cela est enfin réalisable.)
L'utilisation de detach()
fait que l'interpréteur affiche un avertissement lorsqu'il essaie de fermer stdout juste avant de quitter:
Exception ignored in: <_io.TextIOWrapper mode='w' encoding='UTF-8'>
ValueError: underlying buffer has been detached
Au lieu de cela, cela a bien fonctionné pour moi:
default_out = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
(Et, bien sûr, en écrivant à default_out
au lieu de stdout.)
sys.stdout est en mode texte dans Python 3. Par conséquent, vous y écrivez directement unicode, et l'idiome pour Python 2 n'est plus nécessaire).
Où cela échouerait dans Python 2:
>>> import sys
>>> sys.stdout.write(u"ûnicöde")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode character u'\xfb' in position 0: ordinal not in range(128)
Cependant, cela fonctionne simplement dandy dans Python 3:
>>> import sys
>>> sys.stdout.write("Ûnicöde")
Ûnicöde7
Maintenant, si votre Python ne sait pas ce que votre codage stdouts est réellement, c'est un problème différent, très probablement dans la construction de Python.