web-dev-qa-db-fra.com

Définition du codage correct lors du piping stdout dans Python

Lors du transfert de la sortie d'un programme Python, l'interpréteur Python ne comprend pas l'encodage et le définit sur None. Cela signifie un programme comme celui-ci:

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

fonctionnera correctement si vous l’utilisez normalement, mais échouez avec:

UnicodeEncodeError: le codec 'ascii' ne peut pas coder le caractère u '\ xa0' en position 0: l'ordinal n'est pas dans la plage (128)

lorsqu'il est utilisé dans une séquence de tuyaux.

Quel est le meilleur moyen de faire en sorte que cela fonctionne lors de la tuyauterie? Puis-je simplement lui dire d'utiliser tout ce qui est codé par le shell/système de fichiers/ce qu'il utilise?

Les suggestions que j'ai vues jusqu'ici consistent à modifier votre site.py directement ou à coder en dur le codage par défaut à l'aide de ce hack:

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

Y a-t-il une meilleure façon de faire fonctionner la tuyauterie?

321
Joakim Lundborg

Votre code fonctionne lorsqu'il est exécuté dans un script car Python code la sortie en fonction de l'encodage utilisé par votre application de terminal. Si vous jouez, vous devez l'encoder vous-même.

En règle générale, utilisez toujours Unicode en interne. Décodez ce que vous recevez et encodez ce que vous envoyez.

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

Un autre exemple didactique est un programme Python permettant la conversion entre ISO-8859-1 et UTF-8, en mettant tout en majuscule entre les deux.

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)

La définition du codage par défaut du système est une mauvaise idée, car certains modules et bibliothèques que vous utilisez peuvent s’appuyer sur le fait qu’il s’agit d’un code ASCII. Ne le fais pas.

155
nosklo

Tout d'abord, concernant cette solution:

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

Il n’est pas pratique d’imprimer explicitement à chaque fois avec un encodage donné. Ce serait répétitif et sujet aux erreurs.

Une meilleure solution consiste à changer sys.stdout au début de votre programme, pour coder avec le codage sélectionné. Voici une solution que j'ai trouvée sur Python: Comment choisir sys.stdout.encoding? , en particulier un commentaire de "toka":

_import sys
import codecs
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
_
167
Craig McQueen

Vous voudrez peut-être essayer de remplacer la variable d'environnement "PYTHONIOENCODING" par "utf_8". J'ai écrit un page sur mon épreuve avec ce problème .

Tl; dr de l'article de blog:

import sys, locale, os
print(sys.stdout.encoding)
print(sys.stdout.isatty())
print(locale.getpreferredencoding())
print(sys.getfilesystemencoding())
print(os.environ["PYTHONIOENCODING"])
print(chr(246), chr(9786), chr(9787))

vous donne

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

faire le travail, mais ne peut pas le définir sur python lui-même ...

ce que nous pouvons faire est de vérifier si le paramètre n'est pas défini et d'indiquer à l'utilisateur de le définir avant le script d'appel avec:

if __== '__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)

Mise à jour pour répondre au commentaire: le problème n’existe que lors de l’acheminement vers stdout. J'ai testé dans Fedora 25 Python 2.7.13

python --version
Python 2.7.13

chat b.py

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

print sys.stdout.encoding

en cours d'exécution ./b.py

UTF-8

en cours d'exécution ./b.py | Moins

None
60
Sérgio

J'ai eu un problème similaire la semaine dernière . C'était facile à résoudre dans mon IDE (PyCharm).

Voici ma solution:

À partir de la barre de menus de PyCharm: Fichier -> Paramètres ... -> Éditeur -> Codages de fichiers, puis définissez: "Codage IDE", "Codage de projet" et "Codage par défaut pour les fichiers de propriétés" TOUT à UTF-8 et elle fonctionne maintenant. comme un charme.

J'espère que cela t'aides!

5
CLaFarge

Une version assainissable discutable de la réponse 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

Usage:

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

Je pourrais "automatiser" avec un appel à:

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

Oui, il est possible d'obtenir une boucle infinie ici si cette "setenv" échoue.

2
jno

J'ai rencontré ce problème dans une application héritée, et il était difficile d'identifier où ce qui était imprimé. Je me suis aidé avec ce hack:

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

En plus de mon script, test.py:

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

Notez que ceci modifie TOUS les appels à imprimer pour utiliser un codage, ainsi votre console l’imprimera:

$ python test.py
b'Axwell \xce\x9b Ingrosso'
1
cessor

Je pensais juste que je mentionnerais quelque chose ici que je devais passer beaucoup de temps à expérimenter avant de finalement comprendre ce qui se passait. Cela peut sembler tellement évident à tout le monde ici qu’ils n’ont pas pris la peine de le mentionner. Mais cela m'aurait aidé s'ils l'avaient fait, alors sur ce principe ...!

NB: J'utilise Jython spécifiquement, v 2.7, donc cela pourrait ne pas s'appliquer à CPython ...

NB2: les deux premières lignes de mon fichier .py sont les suivantes:

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

Le mécanisme de construction de chaîne "%" (AKA "opérateur d'interpolation" ") pose également des problèmes supplémentaires ... Si le codage par défaut de" l'environnement "est ASCII et que vous essayez de faire quelque chose comme

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

Vous n'aurez aucune difficulté à exécuter Eclipse ... Dans une CLI Windows (fenêtre DOS), vous constaterez que le codage est page de codes 85 (mon système d’exploitation Windows 7) ou quelque chose de similaire, capable de gérer les fichiers européens. caractères accentués au moins, donc ça va marcher.

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

travaillera également.

Si, OTOH, vous dirigez vers un fichier à partir de la CLI, le codage stdout sera None, qui sera par défaut à ASCII (sur mon système d’exploitation de toute façon), qui ne pourra gérer aucune des impressions précédentes. ... (erreur d'encodage redoutée).

Alors, vous pourriez penser à rediriger votre stdout en utilisant

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

et essayez d’exécuter dans la tuyauterie CLI vers un fichier ... Très curieusement, l’impression A ci-dessus fonctionnera ... Mais l’impression B ci-dessus générera une erreur d’encodage! Ce qui suit fonctionnera toutefois correctement:

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

La conclusion à laquelle je suis arrivé (provisoirement) est que si une chaîne spécifiée comme chaîne nicode utilisant le préfixe "u" est soumise au mécanisme% -handling, elle semble impliquer l'utilisation de le codage d’environnement par défaut, , que vous ayez défini ou non stdout pour la redirection!

La façon dont les gens traitent cela est une question de choix. Je souhaiterais qu'un expert Unicode dise pourquoi cela se produit, si je me suis trompé d'une manière ou d'une autre, quelle est la solution à privilégier, à savoir si elle s'applique également à CPython , si cela se produit dans Python 3, etc., etc.

1
mike rodent

Sur Ubuntu 12.10 et le terminal GNOME, aucune erreur n’est générée lorsque le programme est imprimé sur stdout ou raccordé à un canal pour d’autres programmes. Le codage de fichier et le codage de terminal sont tous deux TF-8 .

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

Quel système d'exploitation et émulateur de terminal utilisez-vous? J'ai entendu dire que certains de mes collègues avaient des problèmes similaires avec iTerm 2 et OS X; iTerm 2 peut être le coupable.

Mise à jour: Cette réponse est fausse - voir les commentaires pour plus de détails

1
Fish Monitor