J'écris un programme Python (Python 3.3) pour envoyer des données à une page Web à l'aide de la méthode POST. Surtout pour le processus de débogage, j'obtiens le résultat de la page et l'affiche à l'écran à l'aide de la fonction print()
.
Le code est comme ça:
conn.request("POST", resource, params, headers)
response = conn.getresponse()
print(response.status, response.reason)
data = response.read()
print(data.decode('utf-8'));
la méthode HTTPResponse
.read()
renvoie un élément bytes
codant la page (document UTF-8 bien formaté). Il semblait bien jusqu'à ce que j'arrête d'utiliser IDLE GUI for Windows et utilise la console Windows. au lieu. La page renvoyée a un caractère U + 2014 (em-dash) que la fonction d'impression traduit bien dans l'interface graphique de Windows (je présume que la page de code est Page 1252), mais pas dans la console Windows (page de code 850). Étant donné le comportement par défaut de strict
, j'obtiens l'erreur suivante:
UnicodeEncodeError: 'charmap' codec can't encode character '\u2014' in position 10248: character maps to <undefined>
Je pourrais le réparer en utilisant ce code assez moche:
print(data.decode('utf-8').encode('cp850','replace').decode('cp850'))
Maintenant, il remplace le caractère incriminé "-" par un ?
. Ce n'est pas le cas idéal (un trait d'union devrait être un meilleur remplaçant) mais suffisant pour mon propos.
Il y a plusieurs choses que je n'aime pas dans ma solution.
Le problème n’est pas l’emdash (je peux penser à plusieurs façons de résoudre ce problème), mais j’ai besoin d’écrire du code robuste. Je nourris la page avec les données d'une base de données et ces données peuvent revenir. Je peux anticiper bien d'autres cas conflictuels: un 'Á' U + 00c1 (possible dans ma base de données) pourrait être traduit en CP-850 (codage de la console DOS/Windows pour les langues d'Europe occidentale) mais pas en CP-437 (codage pour les États-Unis). Anglais, qui est utilisé par défaut dans beaucoup d’installations Windows).
Alors, la question:
Existe-t-il une solution plus agréable qui rend mon code indépendant du codage de l'interface de sortie?
Je vois trois solutions à cela:
Modifiez le codage de sortie afin qu’il génère toujours le format UTF-8. Voir par exemple Réglage de l'encodage correct lors du piping de la sortie standard en Python , mais je n'ai pas réussi à faire fonctionner ces exemples.
L'exemple de code suivant permet à la sortie de connaître votre jeu de caractères cible.
# -*- coding: utf-8 -*-
import sys
print sys.stdout.encoding
print u"Stöcker".encode(sys.stdout.encoding, errors='replace')
print u"Стоескер".encode(sys.stdout.encoding, errors='replace')
Cet exemple remplace correctement tout caractère non imprimable de mon nom par un point d'interrogation.
Si vous créez une fonction d’impression personnalisée, par exemple, appelé myprint
, en utilisant ces mécanismes pour coder correctement la sortie, vous pouvez simplement remplacer print par myprint
là où cela est nécessaire sans que tout le code ne paraisse moche.
Réinitialisez l'encodage de sortie globalement au début du logiciel:
La page http://www.macfreek.nl/memory/Encoding_of_Python_stdout présente un bon résumé de la procédure à suivre pour modifier le codage de sortie. La section "StreamWriter Wrapper around Stdout" est particulièrement intéressante. En gros, il est dit de changer la fonction d'encodage d'E/S comme ceci:
Dans Python 2:
if sys.stdout.encoding != 'cp850':
sys.stdout = codecs.getwriter('cp850')(sys.stdout, 'strict')
if sys.stderr.encoding != 'cp850':
sys.stderr = codecs.getwriter('cp850')(sys.stderr, 'strict')
Dans Python 3:
if sys.stdout.encoding != 'cp850':
sys.stdout = codecs.getwriter('cp850')(sys.stdout.buffer, 'strict')
if sys.stderr.encoding != 'cp850':
sys.stderr = codecs.getwriter('cp850')(sys.stderr.buffer, 'strict')
Si utilisé dans la sortie HTML de CGI, vous pouvez remplacer "strict" par "xmlcharrefreplace" pour obtenir des balises codées HTML pour les caractères non imprimables.
N'hésitez pas à modifier les approches, en définissant différents codages, .... Notez que cela ne fonctionnera toujours pas pour la sortie de données non spécifiées. Donc, toutes les données, les entrées, les textes doivent être correctement convertibles en unicode:
# -*- coding: utf-8 -*-
import sys
import codecs
sys.stdout = codecs.getwriter("iso-8859-1")(sys.stdout, 'xmlcharrefreplace')
print u"Stöcker" # works
print "Stöcker".decode("utf-8") # works
print "Stöcker" # fails
Basé sur la réponse de Dirk Stöcker, voici une fonction d’emballage soignée pour la fonction d’impression de Python 3. Utilisez-le comme vous utiliseriez l'impression.
En prime, comparé aux autres réponses, cela n'imprimera pas votre texte sous forme de repère ('b "contenu"'), mais sous forme de chaînes normales ("contenu"), en raison de la dernière étape de décodage.
def uprint(*objects, sep=' ', end='\n', file=sys.stdout):
enc = file.encoding
if enc == 'UTF-8':
print(*objects, sep=sep, end=end, file=file)
else:
f = lambda obj: str(obj).encode(enc, errors='backslashreplace').decode(enc)
print(*map(f, objects), sep=sep, end=end, file=file)
uprint('foo')
uprint(u'Antonín Dvořák')
uprint('foo', 'bar', u'Antonín Dvořák')
Pour le débogage, vous pouvez utiliser print(repr(data))
.
Pour afficher du texte, imprimez toujours Unicode. Ne codez pas le codage de caractères de votre environnement tel que cp850
dans votre script. Pour décoder la réponse http, voir n bon moyen d’obtenir le jeu de caractères/l’encodage d’une réponse HTTP en Python .
Pour imprimer la console Unicode sur Windows, vous pouvez tiliser le package win-unicode-console
] .
J'ai creusé plus profondément dans ceci et trouvé les meilleures solutions sont ici.
http://blog.notdot.net/2010/07/Getting-unicode-right-in-Python
Dans mon cas, j'ai résolu "UnicodeEncodeError: le codec 'charmap' ne peut pas encoder de caractère"
code d'origine:
print("Process lines, file_name command_line %s\n"% command_line))
Nouveau code:
print("Process lines, file_name command_line %s\n"% command_line.encode('utf-8'))
Si vous utilisez la ligne de commande Windows pour imprimer les données, vous devez utiliser
chcp 65001
Cela a fonctionné pour moi!
Si vous utilisez Python 3.6 (éventuellement 3.5 ou ultérieur), cette erreur ne me sera plus transmise. J'avais un problème similaire, car j'utilisais la v3.4, mais il est parti après ma désinstallation et ma réinstallation.