189 votes

UnicodeEncodeError: le codec 'charmap' ne peut pas encoder - les mappages de caractères sur <undefined> , fonction d'impression

Je suis en train d'écrire un Python (Python 3.3) programme pour envoyer des données à une page web à l'aide de la méthode POST. Surtout pour le processus de débogage, j'obtiens la page de résultat et l'afficher sur l'écran à l'aide d' print() fonction.

Le code ressemble à ceci:

conn.request("POST", resource, params, headers)
response = conn.getresponse()
print(response.status, response.reason)
data = response.read()
print(data.decode('utf-8'));

l' HTTPResponse .read() méthode retourne un bytes élément de codage de la page (qui est bien formaté en UTF-8 document), Il semblait correct jusqu'à ce que je me suis arrêté à l'aide d'INACTIVITÉ GUI pour Windows et utilisé la console Windows à la place. Le retour de la page a un U+2014 de caractères (tiret cadratin) dont la fonction d'impression traduit bien dans l'interface utilisateur Windows (je suppose que la Page de Code 1252) mais ce n'est pas dans la Console Windows (la Page de Code 850). Compte tenu de l' strict comportement par défaut j'obtiens l'erreur suivante:

UnicodeEncodeError: 'charmap' codec can't encode character '\u2014' in position 10248: character maps to <undefined>

J'ai pu le résoudre en utilisant ce assez moche code:

print(data.decode('utf-8').encode('cp850','replace').decode('cp850'))

Maintenant il remplacer la délinquance caractère "-" avec un ?. Pas le cas idéal (un trait d'union doivent être mieux de remplacement), mais assez bon pour mon but.

Il y a plusieurs choses que je n'aime pas à partir de ma solution.

  1. Le code est moche avec tout ce que le décodage, de codage et de décodage.
  2. Il résout le problème pour ce cas. Si je port le programme pour un système utilisant un autre encodage (latin-1, cp437, dos à cp1252, etc.) il convient de reconnaître la cible de codage. Il ne le fait pas. (par exemple, en utilisant de nouveau le RALENTI GUI, le emdash est également perdu, qui n'était pas le cas avant)
  3. Il serait plus agréable si la emdash traduit un trait d'union au lieu d'une interrogation bang.

Le problème n'est pas la emdash (je peux penser à plusieurs façons de résoudre particulièrement problème) mais j'ai besoin d'écrire du code robuste. Je suis l'alimentation de la page avec des données provenant d'une base de données et que les données peuvent revenir. Je peux anticiper de nombreux autres cas de conflit: un 'Á' U+00c1 (ce qui est possible dans ma base de données) pourrait se traduire par des CP-850 (DOS/Windows Console encodign pour les Langues d'europe Occidentale), mais pas en CP-437 (codant pour l'anglais AMÉRICAIN, qui est par défaut dans de nombreuses installations de Windows).

Donc, la question:

Est-il une meilleure solution que fait mon code agnostique à partir de la sortie de l'interface d'encodage?

117voto

Dirk Stöcker Points 426

Je vois trois solutions à ce problème:

  1. Changer l'encodage de sortie, de sorte qu'il sera toujours la sortie de l'UTF-8. Voir, par exemple, le Réglage de l'encodage lors de la tuyauterie de sortie standard (stdout) en python, mais je ne pouvais pas obtenir ces exemple de travail.

  2. Exemple de code suivant fait la sortie de courant de votre cible de jeu de caractères.

    # -*- 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 correctement remplace n'importe quel caractère non imprimable en mon nom avec un point d'interrogation.

    Si vous créez une fonction d'impression personnalisée, par exemple appelé myprint, en utilisant que des mécanismes de coder la sortie correctement, vous pouvez simplement remplacer l'imprimer avec myprint là où nécessaire sans code moche.

  3. Réinitialisation de l'encodage en sortie à l'échelle mondiale au début du logiciel:

    La page http://www.macfreek.nl/memory/Encoding_of_Python_stdout a un bon résumé de quoi faire changer d'encodage de sortie. En particulier la section "StreamWriter Wrapper autour de la sortie standard Stdout" est intéressant. Essentiellement, il dit de changer le I/O fonction d'encodage comme ceci:

    En 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')
    

    En 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 elle est utilisée en CGI de la sortie HTML, vous pouvez remplacer "stricte" par "xmlcharrefreplace' pour obtenir HTML balises codées pour les caractères non-imprimables.

    N'hésitez pas à modifier les approches, les différents encodages, .... Notez qu'il continue de coutume de travail à la sortie non de données spécifié. Donc, toutes les données, les entrées, les textes doivent être correctement convertible 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
    

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X