web-dev-qa-db-ja.com

UnicodeEncodeError: 'charmap'コーデックはエンコードできません - <未定義>への文字マップ、印刷関数

私はPOSTメソッドを使ってWebページにデータを送るためのPython(Python 3.3)プログラムを書いています。主にデバッグ処理のために、ページの結果を取得してprint()関数を使用して画面に表示しています。

コードは次のとおりです。

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

HTTPResponse.read()メソッドはページをエンコードするbytes要素を返します(これは整形式のUTF-8文書です)。Windows用IDLE GUIの使用をやめて代わりにWindowsコンソールを使用するまでは問題ないようでした。返されたページにはU + 2014文字(emダッシュ)が含まれていますが、これは印刷機能ではWindows GUI(コードページ1252と推定)では正しく変換されますが、Windowsコンソール(コードページ850)では変換されません。 strictのデフォルトの振る舞いを考えると、次のようなエラーが出ます。

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

この醜いコードを使って修正することができます。

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

現在は、問題のある文字「 - 」が?に置き換えられています。理想的なケースではありません(ハイフンはより良い代替品になるはずです)が、私の目的には十分です。

私の解決策から好きではないことがいくつかあります。

  1. そのコードは、そのすべてのデコード、エンコード、およびデコードに関して醜いです。
  2. これはまさにこの場合の問題を解決します。他のエンコーディング(latin-1、cp437、cp1252に戻るなど)を使用してシステムにプログラムを移植した場合は、ターゲットのエンコーディングを認識するはずです。そうではありません。 (たとえば、IDLE GUIを再び使用すると、emdashも失われますが、これは以前には発生していませんでした)
  3. Emdashが質問バングの代わりにハイフンに翻訳されていればもっと良いでしょう。

問題はemdashではありません(その問題を解決するためのいくつかの方法を考えることができます)が、堅牢なコードを書く必要があります。データベースからのデータをページに入力しているので、そのデータが戻ってくる可能性があります。私は他の多くの矛盾するケースを予想することができます: 'Á' U + 00c1(私のデータベースでは可能です)はCP-850(西ヨーロッパ言語用のDOS/Windowsコンソールエンコーディング)に変換できますが英語、多くのWindows版ではこれがデフォルトです。

だから、質問:

私のコードを出力インターフェースのエンコーディングとは無関係にするより良い解決策はありますか?

これには3つの解決策があります。

  1. 出力エンコーディングを変更して、常にUTF-8が出力されるようにします。例えば参照。 Python で標準出力をパイプ処理するときに正しいエンコーディングを設定していますが、これらの例をうまく機能させることができませんでした。

  2. 次のコード例では、出力にターゲットの文字セットを認識させます。

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

    この例では、私の名前の中の印刷不能文字を疑問符で正しく置き換えます。

    カスタム印刷機能を作成する場合myprintと呼ばれ、そのメカニズムを使用して出力を正しくエンコードすることで、コード全体を見にくくすることなく、必要に応じてprintをmyprintに置き換えることができます。

  3. ソフトウェアの開始時に出力エンコーディングをグローバルにリセットします。

    http://www.macfreek.nl/memory/Encoding_of_Python_stdout ]ページには、出力エンコーディングを変更するために何をすべきかについての良い要約があります。特に「Stdoutの周りのStreamWriterラッパー」セクションは興味深いです。基本的にそれはこのようにI/Oエンコーディング機能を変更することを言います:

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

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

    HTMLを出力するCGIで使用する場合は、 'strict'を 'xmlcharrefreplace'に置き換えて、印刷不能文字用のHTMLエンコードタグを取得できます。

    アプローチを変更したり、異なるエンコーディングを設定したりしてください。指定されていないデータを出力するのはまだうまくいきません。そのため、データ、入力、テキストはすべて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
    
99
Dirk Stöcker

DirkStöckerの答えに基づいて、これがPython 3のprint関数のためのきちんとしたラッパー関数です。 printを使うのと同じようにそれを使ってください。

追加のボーナスとして、他の答えと比較して、これはあなたのテキストをバイト配列( 'b "content")としてではなく、通常の文字列(' content ')として印刷しません、最後のデコードステップのためです。

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')
28
Jelle Fresen

デバッグ目的で、print(repr(data))を使うことができます。

テキストを表示するには、常にUnicodeを印刷してください。スクリプト内のcp850など、環境の文字エンコードをハードコードしないでください。 http応答をデコードするには、 を参照してください。PythonでHTTP応答の文字セット/エンコーディングを取得する

UnicodeをWindowsコンソールに表示するには、 win-unicode-consoleパッケージ を使用できます。

25
jfs

私はこれについてもっと深く掘り下げ、最良の解決策がここにあることを見出しました。

http://blog.notdot.net/2010/07/Getting-unicode-right-in-Python

私の場合は、「UnicodeEncodeError: 'charmap'コーデックは文字をエンコードできません」を解決しました。

元のコード

print("Process lines, file_name command_line %s\n"% command_line))

新しいコード

print("Process lines, file_name command_line %s\n"% command_line.encode('utf-8'))  
18
leemonq

Windowsのコマンドラインを使ってデータを印刷している場合は、

chcp 65001

これは私のために働いた!

15
Željko Krnjić

あなたがPython 3.6(おそらく3.5以降)を使っているなら、それはもう私にはそのエラーを与えません。私はv3.4を使用していたので、私は同様の問題を抱えていましたが、私がアンインストールして再インストールした後にそれは消えました。

1
Solumyr