wiki:PythonJISX0213

Pythonで「㍑」でハマる

ダメ
>>> u'\u3351'.encode('iso-2022-jp')
>>> u'\u3351'.encode('sjis')
>>> u'\u3351'.encode('euc-jp')
うまくいく
>>> u'\u3351'.encode('utf-8')
>>> u'\u3351'.encode('cp932')
>>> u'\u3351'.encode('iso-2022-jp-2004')
>>> u'\u3351'.encode('eucjis2004')

「\u3351」は「㍑」です。

 Python内蔵のコーデックは割とクソまじめで、エンコーディング方式の微妙な違いをちゃんと区別する。たとえば「iso-2022-jp」「sjis」「euc-jp」は ISO-2022-JP相当の文字しか表現できない。ここには、通称「JIS2004」の JIS X 0213で追加された文字を含まない。このため、「㍑」をエンコードできなくてエラーになる。

電子メールやウェブなど、ネット上の文字のやりとりでは、メタ情報にエンコーディング方式の名称を含めるのが一般的(charsetの話)。 charsetiso-2022-jp-2004 が指定されたデータは見たことが無い(IANAに登録完了してないからあたりまえか)。 でも、電子メールでもウェブでも「㍑」などのJIS2004の文字をやりとりできるのが普通。 つまり、

  • charsetにはiso-2022-jpが指定されている。
  • 「㍑」などJIS2004の文字をエンコードできてしまう。
  • エスケープシーケンスは、iso-2022-jpのやり方。「㍑」は「\033$B-H\033(B」。

という、デタラメなことになっている。 これがいわゆる「機種依存文字を使うな」と言われる問題。 「㍑」は今や「機種依存文字」ではないのにね。

Pythonで電子メールやウェブのデータを処理しようとして、charsetiso-2022-jp と書いてあったからと言って、素直にiso-2022-jpでデコードしようとすると、「㍑」を含んでいるとエラーになる。しかし、iso-2022-jp-2004でデコードしてもエラーになる。なぜならば、JIS2004に厳密に従うならば、上記の例の「\033$B-H\033(B」というデータは存在し得ないから。

苦肉の策、別名「ハック」。

>>> decoded = encoded.replace('\033$B', '\033$(Q').decode('iso-2022-jp-2004')
>>> encoded = decoded.encode('iso-2022-jp-2004').replace('\033$(Q', '\033$B')

ネット上の日本語データの取り扱いに適した、ゆるいコーデックが欲しいところ。

めんどくさい。そもそも、ISO-2022-JPを決めるときに半角カナを排除しやがったのが…(違う話か)。 なんだかんだ言っても、UTF-8でやりとりするのが一番楽かもね。 メールもUTF-8で送りゃいいだろって言う外国人が正しく思えてきた。 あ、「~」がマックとウィンドウズで逆に表示される問題があるんだっけ。

そういえば、昔は良くも悪くもISO-2022-JPを制定するなど、規格化しようとがんばった人たちが居たんだけど、最近はそういう人が居ないね。 MUAなり、ブラウザなり、携帯なり、製品の実装ありきandそれしか無いのが今の世の中。 規格が無いものだから、デタラメで自分勝手な実装ばかり現れておかしな事に成る。 だって、「このメールソフトは規格に厳密なので、『㍑』や『①』は書けません」なんてメールソフトは、誰も使いたくないもんね。

携帯やさんとか、グーグルとかヤフーとか、ネットで商売している会社が集まって提案してくれないかなあ。 今は、学者さんが理想を現実に持ち込む時代とは違うというのは、確かにそう思う。

(2009/4/4 - sgk)