Python3でUnicodeDecodeErrorに遭遇したときのTODOリスト


落ち着く

深呼吸しましょう。焦りは禁物です。苛立ちは憎悪へと変わり自らを悔い改める機会を失うかもしれません。丹田に手を当てて鼻から息を吸って・・・「Fuxk!!!!」などと叫んではいけません。言葉遣いに気を付けましょう。

目的の文字コードを決める

深呼吸が済んだら目的としていた文字コードを思い出します。あなたはきっとこう呟く・・・UTF-8・・・UTF-8・・・。おめでとうございます。これでもう道に迷うことはありませんね。

端末の文字コードを確認する

旅立ちの前にあなたの良き伴侶となっているであろう端末に語りかけます。お前の文字コードは・・・UTF-8・・・UTF-8・・・。端末は答えてくれましたか? 私のはクリックミスで答えてくれませんでした。もう一度深呼吸をしましょう。それからLANG環境変数も確認しておきましょう。

$ echo $LANG

何も表示されない場合は環境に文字コードが設定されていない可能性があります。
環境に言語パックをインストールしてLANG環境変数に設定しましょう。
Ubuntuの場合は↓のようにすれば設定できます(コメント欄から引用)。

apt install -y language-pack-ja
export LANG=ja_JP.UTF-8

入力元の文字コードを確認する

ファイルであればファイルの文字コードを確認しましょう。そもそものファイルが文字化けしていませんか? 私のは文字化けしていました。悔い改めましょう。
パイプであれば入力ストリームの文字コードを確認しましょう。curlやwgetを使っているなら尚更です。文字コードの変換は間にiconvを挟むと良いでしょう。ストリームは文字化けしていませんか? やはり私のは文字化けしていました。失ったのは貴重な一日ですが、この一日がきっと自らを前に進めてくれることでしょう。

スクリプトの文字コードを確認する

スクリプト・ファイルの保存文字コード、それからコードのエンコード指定を確認してみましょう。

# -*- coding: utf-8 -*-

さいきんのPythonではデフォルトエンコーディングを使う場合はこの指定をしないのがナウイみたいです。
cp932とか使いたい場合は指定するようにしましょう。

ちなみにPythonのデフォルトエンコーディングは↓のコードで確認できます。

import sys

print('defaultencoding:', sys.getdefaultencoding())

Python3に語りかける

旅の最後はPython3と共に歩みます。長かったですね。あなたはきっと「ここまでお膳立てしてあげたんだから・・・」などと淡い期待を抱くかもしれません。しかし残念ながら飛んできたのはException・・・同じみのアイツです。Python3のprint()などはsys.stdoutを内部で使っています。標準入出力用のファイルオブジェクトは特定の文字コードでラップしておくのが安心です。

import sys
import io

sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8')
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')

ファイルならopen関数にencodingを指定しましょう。

with open('myfile.txt', mode='rt', encoding='utf-8') as fin:
    content = fin.read()
    print(content)

PYTHONIOENCODINGを設定する

環境変数のPYTHONIOENCODINGに文字コードを設定することで実行するスクリプトの標準入出力の文字コードを設定することが出来ます。
io.TextIOWrapperがめんどくさいあなたへ。

# 標準出力のエンコーディングの確認
import sys
print(sys.stdout.encoding)
$ PYTHONIOENCODING=utf-8 python script.py
utf-8

おわりに

これでもまだ例のアイツが現れたら・・・。散歩に行きましょう :-D

参考

UnicodeEncodeError: に悩まされない。Python2.x から Python3.x への乗り換え