エンコーディング指定

入出力周りのエンコーディングの設定の方法が書かれていた。この例で、エンコーディングを取得する前半と入出力のエンコーディングを設定する後半に分けて考える。

import sys, locale, codecs

enc = locale.getpreferredencoding()
sys.stdout = codecs.getwriter(enc)(sys.stdout)
sys.stdin = codecs.getreader(enc)(sys.stdin)

Pythonチュートリアル』には以下のコードが載っていた。

import codecs, sys
sys.stdout = codecs.lookup('utf-8')[-1](sys.stdout)

後半部分は、以下のように同じことをやっているだけ。

>>> import codecs
>>> codecs.lookup('utf-8')
<codecs.CodecInfo object for encoding utf-8 at 0x1437d88>
>>> list(codecs.lookup('utf-8'))
[<built-in function utf_8_encode>,
 <function decode at 0x014E37F0>,
 <class encodings.utf_8.StreamReader at 0x0143BED0>,
 <class encodings.utf_8.StreamWriter at 0x0143BEA0>]
>>>
>>> codecs.getwriter('utf-8')
<class encodings.utf_8.StreamWriter at 0x0143BEA0>


前半のlocale.getpreferredencoding()は、ユーザが使用していると推測されるcharsetを取得している。これをそのまま指定した場合に問題が発生するケースが書かれた以下の記事を見つけた。


Tracでは、前半部分を以下のようにしているみたい。但し、単なる安全対策っぽいのであまり本質的ではないかも。

...(略)...
encoding = locale.getpreferredencoding() or sys.getdefaultencoding()
if sys.platform != 'win32':
    encoding = locale.getlocale(locale.LC_TIME)[1] or encoding
    # the above is broken on win32, e.g. we'd get '437' instead of 'cp437'
...(略)...

但し、気になったのは、MAC OS XPython 2.5.1をソースからインストールした場合に発生した以下のような問題点。

  • locale.getpreferredencoding() の返すエンコーディングが X-MAC-JAPANESE になってしまう
  • Python では X-MAC-JAPANESE がデフォルトではサポートされていないため、locale.getpreferredencoding() を利用したエンコード、デコードができない

記事に書かれていたのは回避方法として、locale.pyにパッチを当てるという方法だった。パッチによりgetpreferredencoding()関数定義の分岐を変更したりしている。


まとめると、odz bufferさんの方法は、確かにベストプラクティスかもしれない。但し、getpreferredencoding()関数の内容によってはPythonで使用できないencodingが返ってしまいLookupErrorが発生する可能性はあるということは意識しておく必要はあると思う。


以下の2つのどちらかの対策は必要かもしれない。

  • encをアプリごとにユーザが自分で'utf-8'や'cp932'などと直接指定する
  • LookupErrorが出た場合はユーザにlocale.pyにパッチを当ててもらう