2007-03-07

前回からの続き。

前回の話は、まとめると、テキストファイルをエンコーディングの判定する前に行分けすると UTF-16LE の時にハマるよ、ということで、これはどのプログラムでも共通の話題なのですが、ここからは Python 固有の話です。

ところが調べているうちにちょっと状況というか視点が変わってきちゃいまして……。以下に述べるこれは、codecs モジュールのバグなのではないかという気がしてきたのですが、どうでしょう。清水川さん見てるかなあ。識者の意見がほしいです。

とりあえず、前回「ふたつ問題が」と書いたのは、以下のことを想定していました。

  • ファイル先頭に BOM が付いていることがある(から気をつけたし)。
  • codecs.open で開いたファイルオブジェクトは改行コードが変換されない。

ひとつ目は読んでそのままの内容なのですが、自前で判定するのは難しくないですし、そもそもどうやら Python 2.5 で BOM 付きの codec が追加されたようなのでそんなに問題ではなくなってしまいました。(これを検証するためにようやく 2.5 を入れたのです。)

で、もう一点ですが、codecs.opencodecs.StreamReaderWriter で作ったファイルオブジェクトは、内部的には必要上――前回書いたようなことからくる理由――からファイルをバイナリモードでオープンしているようなのですが、にもかかわらずテキストモードに期待される OS ごとの改行コードの変換処理を肩代わりしていないように思われます。

このことはテキストモード/バイナリモードの区別がない Linux や FreeBSD 等では問題にならないのですが、Windows では困ったことが起こります。

検証。まず以下のようなテキストファイル a.txt を作り、エンコーディング Shift_JIS(なんでもいいですが)、改行コード CRLF で保存する。

abc
あいう

Python インタプリタを起動。

C:¥Documents and Settings¥mshibata¥My Documents>python
Python 2.5 (r25:51908, Sep 19 2006, 09:52:17) [MSC v.1310 32 bit (Intel)] on w
32
Type "help", "copyright", "credits" or "license" for more information.
>>> file('a.txt', 'rb').read()
'abc¥r¥n¥x82¥xa0¥x82¥xa2¥x82¥xa4¥r¥n'
>>> file('a.txt', 'r').read()
'abc¥n¥x82¥xa0¥x82¥xa2¥x82¥xa4¥n'
>>> file('a.txt', 'r').read().decode('shift_jis')
u'abc¥n¥u3042¥u3044¥u3046¥n'
>>> import codecs
>>> codecs.open('a.txt', 'r', 'shift_jis').read()
u'abc¥r¥n¥u3042¥u3044¥u3046¥r¥n'
>>>

ほらあ! やっぱりおかしい。これとは逆に書き込みでは、codecs.open('b.txt', 'w', 'shift_jis').write('hoge hoge hoge¥n') とすると Windows では当然改行コードが ‘¥r¥n’ に置き換えられることを期待するが、それは行われず、結果的に改行コード LF のテキストファイルができてしまう。

“codecs” をキーワードに Python Bug Tracker を検索してみたけど、ステータスが Open になって残っている中ではそれらしい議論は見当たらず。Closed の中には前回書いたようなことと思しき議論はあったけど、それ自体はもう解決しているので今回の話とは違うし。

comp.lang.python には、codecs.open はモード ‘rU’ で開くとなんか変だよね」というような話(たぶん)が見つかったけど、「本来やるべき改行コードの変換をしてない」という視点には行ってないように見えます。

だいたい本来エンコーディングを自前で実装するなら手順的には「読み込み→デコード→改行コード変換」とするべきなのに、歴史的経緯から改行コードの変換は C の fopen に頼ってるもんだから(すみませんソース見たわけじゃないですけどたぶん)、「(読み込み→改行コード変換)→デコード」(括弧内が fopen の管轄)になってしまっているのが悪い。

と、いったような意見に至ったのですが、どうなんでしょう。それにどこに言えばいいんだ? comp.lang.python? それは度胸がいるなぁ……。