Python, Unicode et la console Windows

146

Lorsque j'essaye d'imprimer une chaîne Unicode dans une console Windows, j'obtiens une UnicodeEncodeError: 'charmap' codec can't encode character ....erreur. Je suppose que c'est parce que la console Windows n'accepte pas les caractères Unicode uniquement. Quelle est la meilleure façon de contourner cela? Est-il possible que Python imprime automatiquement un fichier au ?lieu d'échouer dans cette situation?

Edit: J'utilise Python 2.5.


Remarque: la réponse @ LasseV.Karlsen avec la coche est un peu obsolète (à partir de 2008). Veuillez utiliser les solutions / réponses / suggestions ci-dessous avec précaution !!

La réponse @JFSebastian est plus pertinente à partir d'aujourd'hui (6 janvier 2016).

James Sulak
la source
Quelle version de Python utilisez-vous? J'ai vu des références indiquant que cela était cassé dans 2.4.3 et corrigé dans 2.4.4.
Stu
3
en relation: bugs.python.org/issue1602
jfs
Vérifiez ceci .
Soorena le
1
la réponse la plus simple que j'ai trouvée est de taper: chcp 65001 avant d'utiliser pyhton dans cmd
Soorena
1
Ensuite, vous devriez changer votre réponse acceptée ...
Mr_and_Mrs_D

Réponses:

38

Remarque: Cette réponse est en quelque sorte dépassée (à partir de 2008). Veuillez utiliser la solution ci-dessous avec précaution !!


Voici une page qui détaille le problème et une solution (recherchez le texte Wrapping sys.stdout dans une instance ):

PrintFails - Wiki Python

Voici un extrait de code de cette page:

$ python -c 'import sys, codecs, locale; print sys.stdout.encoding; \
    sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout); \
    line = u"\u0411\n"; print type(line), len(line); \
    sys.stdout.write(line); print line'
  UTF-8
  <type 'unicode'> 2
  Б
  Б

  $ python -c 'import sys, codecs, locale; print sys.stdout.encoding; \
    sys.stdout = codecs.getwriter(locale.getpreferredencoding())(sys.stdout); \
    line = u"\u0411\n"; print type(line), len(line); \
    sys.stdout.write(line); print line' | cat
  None
  <type 'unicode'> 2
  Б
  Б

Il y a plus d'informations sur cette page, qui valent la peine d'être lues.

Lasse V. Karlsen
la source
7
Le lien est mort et l'essentiel de la réponse n'a pas été cité. -1
0xC0000022L
1
Lorsque j'essaie les conseils donnés sur l'emballage sys.stdout, il imprime les mauvaises choses. Par exemple, u'\u2013'devient ûau lieu d'un tiret en.
user2357112 prend en charge Monica le
@ user2357112 Vous devrez poster une nouvelle question à ce sujet. Unicode et console système ne sont pas nécessairement la meilleure combinaison, mais je n'en sais pas assez à ce sujet, donc si vous avez besoin d'une réponse définitive, postez une question ici sur SO à ce sujet.
Lasse V. Karlsen
2
le lien est mort. L'exemple de code est incorrect pour la console Windows où la page de codes (OEM) telle que cp437est différente de la page de codes ANSI Windows telle que cp1252. Le code ne corrige pas l' UnicodeEncodeError: 'charmap' codec can't encode charactererreur et peut conduire à mojibake, par exemple, ا©est remplacé silencieusement par ╪º⌐.
jfs
73

Mise à jour: Python 3.6 implémente PEP 528: Changez l'encodage de la console Windows en UTF-8 : la console par défaut sous Windows acceptera désormais tous les caractères Unicode. En interne, il utilise la même API Unicode que le win-unicode-consolepackage mentionné ci-dessous . print(unicode_string)devrait juste fonctionner maintenant.


J'obtiens une UnicodeEncodeError: 'charmap' codec can't encode character... erreur.

L'erreur signifie que les caractères Unicode que vous essayez d'imprimer ne peuvent pas être représentés à l'aide du chcpcodage de caractères de la console actuelle ( ). La page de codes est souvent un codage 8 bits tel quecp437 celui-ci ne peut représenter que ~ 0x100 caractères à partir d'environ 1M de caractères Unicode:

>>> u "\ N {EURO SIGN}". encode ('cp437')
Traceback (dernier appel le plus récent):
...
UnicodeEncodeError: le codec 'charmap' ne peut pas encoder le caractère '\ u20ac' en position 0:
personnage correspond à 

Je suppose que c'est parce que la console Windows n'accepte pas les caractères Unicode uniquement. Quelle est la meilleure façon de contourner cela?

La console Windows accepte les caractères Unicode et peut même les afficher (BMP uniquement) si la police correspondante est configurée . WriteConsoleW()L'API doit être utilisée comme suggéré dans la réponse de @Daira Hopwood . Il peut être appelé de manière transparente, c'est-à-dire que vous n'avez pas besoin et ne devez pas modifier vos scripts si vous utilisez win-unicode-consolepackage :

T:\> py -mpip install win-unicode-console
T:\> py -mrun your_script.py

Voir Quel est le problème avec Python 3.4, Unicode, différents langages et Windows?

Est-il possible que Python imprime automatiquement un fichier au ?lieu d'échouer dans cette situation?

S'il suffit de remplacer tous les caractères non encodables par ?dans votre cas, vous pouvez définir PYTHONIOENCODINGenvvar :

T:\> set PYTHONIOENCODING=:replace
T:\> python3 -c "print(u'[\N{EURO SIGN}]')"
[?]

Dans Python 3.6+, l'encodage spécifié par PYTHONIOENCODING envvar est ignoré pour les tampons de la console interactive, sauf si PYTHONLEGACYWINDOWSIOENCODINGenvvar est défini sur une chaîne non vide.

jfs
la source
3
"la console par défaut sous Windows acceptera désormais tous les caractères Unicode" MAIS vous devez configurer la console: faites un clic droit en haut des fenêtres (de la cmd ou du python IDLE), par défaut / police choisissez la "console Lucida". (Le japonais et le chinois ne fonctionnent pas pour moi, mais je devrais survivre sans cela ...)
JinSnow
2
@Guillaume: la réponse contient la phrase en gras à propos de la console Windows: "si la police correspondante est configurée." Cette réponse ne mentionne pas IDLE mais vous n'avez pas besoin de configurer la police (je vois bien les caractères japonais et chinois dans IDLE par défaut. Essayez print('\u4E01'), print('\u6b63')).
jfs
2
@Guillaume Vous pouvez même obtenir le chinois si vous installez le pack de langue dans Windows 10. Il a ajouté des polices de console prenant en charge le chinois.
Mark Tolonen
28

Malgré les autres réponses plausibles suggérant de changer la page de codes en 65001, cela ne fonctionne pas . (De plus, changer l'encodage par défaut en utilisant sys.setdefaultencodingn'est pas une bonne idée .)

Voir cette question pour les détails et le code qui fonctionne.

Daira Hopwood
la source
2
win-unicode-consoleLe package Python (basé sur votre code) permet d'éviter de modifier votre script s'il imprime directement Unicode à l'aide de la py -mrun your_script.pycommande .
jfs
12

Si vous n'êtes pas intéressé à obtenir une représentation fiable du ou des mauvais caractères, vous pouvez utiliser quelque chose comme ceci (en travaillant avec python> = 2.6, y compris 3.x):

from __future__ import print_function
import sys

def safeprint(s):
    try:
        print(s)
    except UnicodeEncodeError:
        if sys.version_info >= (3,):
            print(s.encode('utf8').decode(sys.stdout.encoding))
        else:
            print(s.encode('utf8'))

safeprint(u"\N{EM DASH}")

Le ou les caractères incorrects de la chaîne seront convertis en une représentation imprimable par la console Windows.

Giampaolo Rodolà
la source
.encode('utf8').decode(sys.stdout.encoding)mène à mojibake eg, u"\N{EM DASH}".encode('utf-8').decode('cp437')->ΓÇö
jfs
print(s.encode('utf-8'))Peut-être simplement un meilleur moyen d'éviter les erreurs du compilateur. Au lieu de cela, vous obtenez une sortie \ xNN pour les caractères non imprimables, ce qui était suffisant pour mes messages de diagnostic.
CODE-REaD
4
C'est extrêmement, spectaculairement faux. L'encodage en UTF-8 puis le décodage en tant que jeu de caractères 8 bits a) échouera souvent, toutes les pages de code n'ont pas de caractères pour les 256 octets, et b) toujours la mauvaise interprétation des données, produisant à la place un désordre Mojibake .
Martijn Pieters
10

Le code ci-dessous rendra la sortie Python vers la console au format UTF-8, même sous Windows.

La console affichera bien les caractères sous Windows 7, mais sous Windows XP, elle ne les affichera pas bien, mais au moins cela fonctionnera et le plus important, vous aurez une sortie cohérente de votre script sur toutes les plates-formes. Vous pourrez rediriger la sortie vers un fichier.

Le code ci-dessous a été testé avec Python 2.6 sur Windows.


#!/usr/bin/python
# -*- coding: UTF-8 -*-

import codecs, sys

reload(sys)
sys.setdefaultencoding('utf-8')

print sys.getdefaultencoding()

if sys.platform == 'win32':
    try:
        import win32console 
    except:
        print "Python Win32 Extensions module is required.\n You can download it from https://sourceforge.net/projects/pywin32/ (x86 and x64 builds are available)\n"
        exit(-1)
    # win32console implementation  of SetConsoleCP does not return a value
    # CP_UTF8 = 65001
    win32console.SetConsoleCP(65001)
    if (win32console.GetConsoleCP() != 65001):
        raise Exception ("Cannot set console codepage to 65001 (UTF-8)")
    win32console.SetConsoleOutputCP(65001)
    if (win32console.GetConsoleOutputCP() != 65001):
        raise Exception ("Cannot set console output codepage to 65001 (UTF-8)")

#import sys, codecs
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
sys.stderr = codecs.getwriter('utf8')(sys.stderr)

print "This is an Е乂αmp١ȅ testing Unicode support using Arabic, Latin, Cyrillic, Greek, Hebrew and CJK code points.\n"
Sorin
la source
1
Existe-t-il un moyen d'éviter cela en utilisant simplement une console différente?
endolith
@sorin: Pourquoi le faites-vous d'abord à l' import win32consoleextérieur d'un tryet plus tard vous le faites conditionnellement à l'intérieur d'un try? N'est-ce pas inutile (le premier import)
0xC0000022L
Pour ce que ça vaut, celui fourni par David-Sarah Hopwood fonctionne (je n'ai même pas fait fonctionner celui-ci car je n'ai pas pris la peine d'installer le module d'extensions win32)
Jaykul
4
Ne changez pas l'encodage par défaut du système; corrigez plutôt vos valeurs Unicode. La modification du codage par défaut peut casser les bibliothèques qui reposent sur le comportement par défaut , vous le savez . Il y a une raison pour laquelle vous devez forcer le rechargement d'un module avant de pouvoir le faire.
Martijn Pieters
7

Entrez simplement ce code en ligne de commande avant d'exécuter le script python:

chcp 65001 & set PYTHONIOENCODING=utf-8
c97
la source
5

Comme la réponse de Giampaolo Rodolà, mais encore plus sale: j'ai vraiment, vraiment l'intention de passer beaucoup de temps (bientôt) à comprendre tout le sujet des encodages et comment ils s'appliquent aux consoles Windoze,

Pour le moment je voulais juste sthg ce qui signifierait que mon programme ne CRASH PAS, et ce que j'ai compris ... et aussi qui n'impliquait pas d'importer trop de modules exotiques (en particulier j'utilise Jython, donc la moitié du temps un Python module s'avère en fait pas disponible).

def pr(s):
    try:
        print(s)
    except UnicodeEncodeError:
        for c in s:
            try:
                print( c, end='')
            except UnicodeEncodeError:
                print( '?', end='')

NB "pr" est plus court à taper que "print" (et un peu plus court à taper que "safeprint") ...!

Mike rongeur
la source
Intelligent, un moyen rapide et sale de contourner le problème. Je pense que c'est génial pour une solution intermittente.
JFA
3

Pour Python 2, essayez:

print unicode(string, 'unicode-escape')

Pour Python 3, essayez:

import os
string = "002 Could've Would've Should've"
os.system('echo ' + string)

Ou essayez win-unicode-console:

pip install win-unicode-console
py -mrun your_script.py
shubaly
la source
2

TL; DR:

print(yourstring.encode('ascii','replace'));

Je suis tombé sur ce problème moi-même, travaillant sur un bot de chat Twitch (IRC). (Python 2.7 dernier)

Je voulais analyser les messages de chat afin de répondre ...

msg = s.recv(1024).decode("utf-8")

mais également les imprimer en toute sécurité sur la console dans un format lisible par l'homme:

print(msg.encode('ascii','replace'));

Cela a corrigé le problème des UnicodeEncodeError: 'charmap'erreurs de lancement du bot et remplacé les caractères Unicode par ?.

Matthew Estock
la source
2

La cause de votre problème n'est PAS la console Win qui ne veut pas accepter Unicode (comme elle le fait puisque je suppose que Win2k par défaut). C'est l'encodage système par défaut. Essayez ce code et voyez ce qu'il vous donne:

import sys
sys.getdefaultencoding()

s'il dit ascii, c'est votre cause ;-) Vous devez créer un fichier appelé sitecustomize.py et le mettre sous le chemin python (je le mets sous /usr/lib/python2.5/site-packages, mais c'est différent sur Win - c'est c: \ python \ lib \ site-packages ou quelque chose), avec le contenu suivant:

import sys
sys.setdefaultencoding('utf-8')

et peut-être voudrez-vous également spécifier l'encodage dans vos fichiers:

# -*- coding: UTF-8 -*-
import sys,time

Edit: plus d'informations peuvent être trouvées dans l'excellent livre Dive into Python

Bartosz Radaczyński
la source
2
setdefaultencoding () n'est plus dans sys (à partir de la v2.0 selon la documentation du module).
Jon Cage
Je ne peux pas le prouver pour le moment, mais je sais que j'ai utilisé cette astuce sur une version ultérieure - 2.5 sur Windows.
Bartosz Radaczyński
6
OK, après un certain temps, j'ai découvert que: "Cette fonction est uniquement destinée à être utilisée par l'implémentation du module site et, si nécessaire, par sitecustomize. Une fois utilisée par le module site, elle est supprimée de l'espace de noms du module sys. "
Bartosz Radaczyński
4
en fait, vous pouvez définir la console Windows sur utf-8. vous devez dire chcp 65001 et ce sera unicode.
Bartosz Radaczyński
4
Pour être tout à fait clair: c'est une très mauvaise idée de changer l'encodage par défaut. Cela revient à cicatriser votre jambe cassée et à marcher comme si de rien n'était, plutôt que de demander à un médecin de régler correctement l'os. Tout le code de gestion du texte Unicode doit le faire de manière cohérente au lieu de s'appuyer sur un codage / décodage implicite.
Martijn Pieters
1

Un peu lié à la réponse de JF Sebastian, mais plus direct.

Si vous rencontrez ce problème lors de l'impression sur la console / le terminal, procédez comme suit:

>set PYTHONIOENCODING=UTF-8
Kinjal Dixit
la source
3
set PYTHONIOENCODING=UTF-8peut conduire à mojibake si la console utilise un encodage différent tel que cp437. cp65001a divers problèmes . Pour imprimer Unicode sur la console Windows, l'API Unicode doit être utilisée ( WriteConsoleW()) comme suggéré dans ma réponsePYTHONIOENCODINGest utilisé uniquement pour remplacer les caractères qui ne peuvent pas être représentés dans la page de codes OEM actuelle par ?( WriteConsoleW()fonctionne même pour de tels caractères). PYTHONIOENCODINGpeut être utilisé si la sortie est redirigée vers un fichier.
jfs
1

Python 3.6 windows7: Il existe plusieurs façons de lancer un python, vous pouvez utiliser la console python (qui a un logo python dessus) ou la console Windows (il est écrit cmd.exe dessus).

Je n'ai pas pu imprimer les caractères utf8 dans la console Windows. L'impression de caractères utf-8 me renvoie cette erreur:

OSError: [winError 87] The paraneter is incorrect 
Exception ignored in: (_io-TextIOwrapper name='(stdout)' mode='w' ' encoding='utf8') 
OSError: [WinError 87] The parameter is incorrect 

Après avoir essayé et échoué à comprendre la réponse ci-dessus, j'ai découvert que ce n'était qu'un problème de réglage. Faites un clic droit en haut des fenêtres de la console cmd, sur l'onglet fontchoisi la console lucida.

J. fait
la source
0

James Sulak a demandé,

Existe-t-il un moyen de faire en sorte que Python imprime automatiquement un? au lieu d'échouer dans cette situation?

D'autres solutions recommandent que nous tentions de modifier l'environnement Windows ou de remplacer la print()fonction de Python . La réponse ci-dessous se rapproche de la demande de Sulak.

Sous Windows 7, Python 3.5 peut être amené à imprimer Unicode sans lancer un UnicodeEncodeErrorcomme suit:

    Au lieu de:     print(text)
    remplacer:     print(str(text).encode('utf-8'))

Au lieu de lancer une exception, Python affiche désormais des caractères Unicode non imprimables sous forme de codes hexadécimaux \ xNN , par exemple:

  Halmalo n \ xe2 \ x80 \ x99 \ xc3 \ xa9tait plus qu \ xe2 \ x80 \ x99un point noir

Au lieu de

  Halmalo n'était plus qu'un point noir

Certes, ce dernier est préférable ceteris paribus , mais sinon le premier est tout à fait exact pour les messages de diagnostic. Dans la mesure où il affiche Unicode sous forme de valeurs d'octets littérales, le premier peut également aider à diagnostiquer les problèmes d'encodage / décodage.

Remarque: l' str()appel ci-dessus est nécessaire car sinon encode(), Python rejette un caractère Unicode en tant que tuple de nombres.

CODE-REaD
la source