私は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'))
現在は、問題のある文字「 - 」が?
に置き換えられています。理想的なケースではありません(ハイフンはより良い代替品になるはずです)が、私の目的には十分です。
私の解決策から好きではないことがいくつかあります。
問題はemdashではありません(その問題を解決するためのいくつかの方法を考えることができます)が、堅牢なコードを書く必要があります。データベースからのデータをページに入力しているので、そのデータが戻ってくる可能性があります。私は他の多くの矛盾するケースを予想することができます: 'Á' U + 00c1(私のデータベースでは可能です)はCP-850(西ヨーロッパ言語用のDOS/Windowsコンソールエンコーディング)に変換できますが英語、多くのWindows版ではこれがデフォルトです。
だから、質問:
私のコードを出力インターフェースのエンコーディングとは無関係にするより良い解決策はありますか?
これには3つの解決策があります。
出力エンコーディングを変更して、常にUTF-8が出力されるようにします。例えば参照。 Python で標準出力をパイプ処理するときに正しいエンコーディングを設定していますが、これらの例をうまく機能させることができませんでした。
次のコード例では、出力にターゲットの文字セットを認識させます。
# -*- 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
に置き換えることができます。
ソフトウェアの開始時に出力エンコーディングをグローバルにリセットします。
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
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')
デバッグ目的で、print(repr(data))
を使うことができます。
テキストを表示するには、常にUnicodeを印刷してください。スクリプト内のcp850
など、環境の文字エンコードをハードコードしないでください。 http応答をデコードするには、 を参照してください。PythonでHTTP応答の文字セット/エンコーディングを取得する 。
UnicodeをWindowsコンソールに表示するには、 win-unicode-console
パッケージ を使用できます。
私はこれについてもっと深く掘り下げ、最良の解決策がここにあることを見出しました。
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'))
Windowsのコマンドラインを使ってデータを印刷している場合は、
chcp 65001
これは私のために働いた!
あなたがPython 3.6(おそらく3.5以降)を使っているなら、それはもう私にはそのエラーを与えません。私はv3.4を使用していたので、私は同様の問題を抱えていましたが、私がアンインストールして再インストールした後にそれは消えました。