BOMに悩む [プログラム三昧]
BOMって、ご存知でしょうか。 電子工作界隈であれば、 "Bill Of Materials" (部品表)なのですが、プログラマ界隈では、 "Byte Order Mark" (バイト順のしるし)の略なのだそうです。
Pythonで日本語を扱いたい
Python という言語で日本語を扱いたい場合、 Python 自身が内部でユニコードを使っていることから、すんなり使えると考えていました。 ところが、実際に使ってみると、 "UTF-8" で書いたはずのファイルが読み込めないという事態に陥ってしまいます。
どうも、ファイルの最初の3バイトに余計なデータが入っているようなのです。
0xef, 0xbb, 0xbf
これは、 Microsoft の Notepad (日本語ではメモ帳)でファイルの先頭に追加される記号です。 これが "Byte Order Mark" (BOM) と呼ばれています。
Python は、BOMを認めていない
Microsoft に偏った情報によると、 BOM の入ったファイルを UTF-8 と呼び、入っていないファイルを UTF-8N と呼ぶ宗派もあるようです。 ところが、 Python 的には、 UTF-8 エンコードというのは、あくまでも BOM の無いファイルを指すのであって、 BOM が入るようなものは UTF-8 とは認めていません。 そのため、 Python に BOM 付きのファイルを読ませようとすると、「ユニコードでは無い文字があるぞ。」と言って受け付けてくれません。
UnicodeEncodeError: 'cp932' codec can't encode character u'\ufeff' in position 0: illegal multibyte sequence
このメッセージでは、 "cp932" に問題があるように見えますが、問題は、 "cp932" では表現できない BOM にあるのです。
とはいえ、抜け道もある
まあ、突っ張っているだけでは、解決しないので、抜け道も用意されています。 それは、 "utf-8-sig" という呪文のようなエンコーディングを使うことです。 このエンコーディングでは、邪魔な BOM を取り除いてファイルを読み込んでくれます。 Notepad で書いた "UTF-8" ファイルを "MS漢字" ファイルに変換するプログラムは、こんな風になります。
fi=codecs.getreader('utf-8-sig')(open('in.txt','r')) fo=codecs.getwriter('cp932')(open('out.txt','w')) for line in fi.readlines(): fo.write(line) fo.close() fi.close()
これで、すっきりしました。
参考文献
- 8.8.2. Encodings and Unicode
- Python のオンラインドキュメントです。 Python は、内部処理こそユニコードを使っていますが、外部ファイルなどとのインターフェイスは、ユニコードばかりとは限りません。 そこで、考えられたのが、エンコーディングという考え方です。 これで、どんな文字コードにも対応できるはずなのですが、反面、エンコーディングの認識に失敗すると、文字化けとなって現れてしまいます。
コメント 0