薄いブログ

技術の雑多なことを書く場所

PythonでUnicodeDecodeError/UnicodeEncodeErrorが出たときの原因調査法

Python2を使っているマルチバイト圏の人間なら一度は遭遇したことがあるであろうUnicodeDecodeError.

スタックトレースに出る情報は

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position 0: ordinal not in range(128)

こういう出力で正直どこで失敗したのかどの変数がどの関数で処理されたときに起こったものなのかが分かりづらい.

これまでは適当にあたりをつけて.decode("utf-8")だったりu"こんにちは"のような感じで試してお茶を濁すことが多かった.

無意識のうちにUnicodeDecodeErrorが持っている情報というのを無視していた.

UnicodeDecodeErrorがどんな情報を持っているのかを説明している文章をあまり見かけないので書いてみようと思った.

6. Built-in Exceptions — Python 2.7.15 documentation

5. Built-in Exceptions — Python 3.7.0 documentation

ここにすべてが書いてある. 公式ドキュメントをちゃんと調査する人間しかたどり着けないようになっている.

python UnicodeDecodeErrorと調べると詰まっている日本人が山程いるし, 場当たり的な解決策を提示した記事が上位にわんさか出てくる.

少しでも時間に余裕があるときは公式ドキュメントを見るようにしたほうが良い.

それはともかくとしてencoding, object, reason, start, end の情報が得られる.

僕自身はutf-8を扱うことが多いので以下のようなコードで調査する.

try:
    raise_unicode_decode_error_function("こんにちは")  # ここはUnicodeDecodeErrorを起こす関数
except UnicodeDecodeError as e:
    print(e.object.decode("utf-8"))

これで対象のデータが分かる.

後はそのデータがどの変数に入っているものなのかどこから来たものなのかの調査ができればあとは適切にencode/decodeするだけだ.

余談

のversion 1.0.0のリリースを作業をしていてCIを追加したり, テストを追加していた際にPython2でのみ

UnicodeDecodeErrorが発生しテストが落ちていたため, 原因調査の際に用いた手法をまとめようと思い書いた記事です.

完全に想定外だったのはevalの中の文字列リテラルがUnicodeDecodeErrorを起こしていたところです.

このへんをPython 2/3 compatibleに書くのってどうしたらいいんでしょうか. 教えてください.

最後にですがembexprをstarしてくれると嬉しいです.