Oliver (4) の日記

2004 年 12 月 19 日
午前 05:59

MySQL 4.1とPerl 5.8と文字コードと

今日は日本各地で/.-J忘年会が行われた。ドイツ時間で朝起きた直後からIRCで中継が始まり、福岡と大阪とはSkypeで会話をしたりした。朝っぱらから頭の中がFatboy SlimによるSlashdotテーマ曲(嘘)「Slash Dot Dash」並に「すらっしゅ」になったので、予定していた休日返上の卒論執筆を棚上げして約半年ぶりに自宅マシンにSlashcode 2.3環境を構築した。

/.-Jで使っているSlashcodeがオリジナルと主に違うのは翻訳されたテンプレートや独自機能をのぞけば、文字の扱いだ。オリジナルのSlashcodeはASCII/Latin1などのシングルバイナなキャラクタエンコーディングを前提にかかれており、あちらこちらにPerlの正規表現の繰り返し回数指定やlengh()を使ったコードが含まれている。とても長いURLの途中に空白を挿入し、「窓が横に延々と延びる」現象に対する対策もそのひとつだ。その様なコードがマルチバイトな文字列に遭遇すると、文字列をぐちゃぐちゃにしてしまう。ユーザによる入力の直後から一貫してEUC-JPを内部コードに使っている現在のSlashcode 2.2.6-Jでは、急いで日本語対応がなされたこともあり、このような文字列を破壊しうるコードパスはすべて無効化されている。

約一年前、Slashcode 2.3化を始めた時には、このような制約を取り払い、キチンとしようと決めた。幸いにも、Perlは5.8へと進化していて、内部的にUnicodeを文字集合として使い、正規表現やlength()をバイト数単位ではなく文字単位で適用することができるようになっていた。春の段階では、文字列破壊がおきうる関数の冒頭でEUC-JPなバイト列から文字列に変換し、処理の後にEUC-JPに戻す、なんてことをやっていた。しかし、このままでは上流に追随するたびに変更ヶ所を細かにチェックする必要があり、大変だ。また、EUC-JPだと文字集合として力不足になりつつあると感じていたこともあり、データベースも含め、内部も完全にUnicode化することを初夏のミーティングで決定した。しかし、そのままタイムアップとなり、卒業試験->卒論の流れが始まったので、/.-J向けにSlashcodeをイヂる人はいなくなってしまった。

今回、再セットアップしたSlashcode環境は総Unicode化にむけたものだ。MySQLは4.1できちんと国際化され、DBや表、カラム単位で収納される文字列の文字集合とエンコーディングを指定できるようになり、クライアントとデータがやりとりする時点で自動的にエンコーディングを変換できるようになった。Debian unstableのMySQLは4.0だったので、experimentalから4.1をゲットし、amd64向けにビルドしたものを使い、まずはデータベースを

CREATE DATABASE slashdotjp DEFAULT CHARACTER SET utf8;

で作成した。次にi386上のMySQL 4.0でEUC-JPのままmysqldumpされたSlashcode 2.3用の試験データの冒頭に

SET NAMES utf8;

を追加したものを新しいデータベースに読み込むと、読み込み時にデータが自動的にUTF-8に変換される。DB直叩きでの日本語を含むSELECTやUPDATEもSET NAMES ujisすればこれまでのEUC-JPな環境と同じ様にできる。スバラシイ!

データがUTF-8になったので、これまでのコード変換ハックを取り除いて、さあこれで問題なし、と思ったが、おもわぬ落とし穴に遭遇してしまった。PerlがUTF-8のデータをバイト列ではなく本当の文字列として扱うにはUTF-8なStringに「UTF-8フラグ」なるものが立ってなければいけないのだ。立っていなければ8-bit ASCIIとみなされ、文字列破壊とその結果の文字化けが発生してしまう。

そして残念ながら、ユーザからHTTP経由でPOSTされてくるデータと違い、DBから入ってくるデータが必ず通る場所はない。中身がUTF-8だとわかっていても、すべてにそれぞれフラグを立てなければならない。これでは個別にコード変換が必要だったEUC-JP時代と変わらない。さて、どうしてたものか。

この問題が解決でりゃ、Slashcode 2.3の公開ベータテストは卒論提出後に残った翻訳やR_2_3_113からR_2_5_xへの追随をがんばれば、比較的すぐにできそうな感じだ。

PS:文字集合としてUnicodeを処理系の内部に使うことは妥当だと思うのだが、MySQLといい、Perlといい。内部での表現方式としてUTF-8を使うのは最悪だ。UTF-32とはいわんから、せめてUTF-16にして欲しかった。サロゲートペアのほうがまだUTF-8よりゃマシだ。ASCII互換とちやほやしてるが、7-bit ASCIIぐらいものもので、Latin-1とも互換性がないことに気がついてますか? Unicode => UTF-8じゃないんですよー。UTF-8よりも醜いものといったらISO-2022ぐらいしかない気がする。

PerlのDBD::MySQLリストにDBからくるデータに強制的にUTF-8フラグをたてるパッチが投稿されている。DBD::Pgに実装済のpg_enable_utf8をモデルにmysql_enable_utf8を追加するものだ。まさしく求めているものといえるが、将来mergeされるかどうか、まったくもって不明なパッチを使うのには抵抗がある。とりあえず試してみるか。

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。

192.168.0.1は、私が使っている IPアドレスですので勝手に使わないでください --- ある通りすがり

処理中...