いっぽんの猟銃のむこうに (DAIZOじいさんとGun)

ヌルめの技術メモとか。フリーランスやってます (http://acez.jp/)

Oracleの波ダッシュ(〜)問題に対処する

まず、UTF-8SJIS文字コードを変換したときに化けやすい文字、ってのがある。
http://ja.wikipedia.org/wiki/Unicode#.E6.B3.A2.E3.83.80.E3.83.83.E3.82.B7.E3.83.A5.E3.83.BB.E5.85.A8.E8.A7.92.E3.83.81.E3.83.AB.E3.83.80.E5.95.8F.E9.A1.8C

そんなかでも有名なのが「〜」。
http://ja.wikipedia.org/wiki/%E6%B3%A2%E3%83%80%E3%83%83%E3%82%B7%E3%83%A5#Unicode.E3.81.AB.E9.96.A2.E9.80.A3.E3.81.99.E3.82.8B.E5.95.8F.E9.A1.8C

でさらに、Oracleの「JAPANESE_JAPAN.JA16SJIS」は、基本はCP932だが「〜」文字(Wave Dash)だけSJIS(\301C)という、なんだかよくわかんない仕様になってる。このへんはいろんな経緯があるみたいでめんどくさそうな感じ。

SQL> select asciistr('〜') from dual;

ASCIISTR('〜')
---------------
\301C

SQL> select asciistr('−') from dual;

ASCIISTR('−')
---------------
\FF0D

これに対処して、「〜」の文字コードをCP932の\FF5Eに統一したのが「JA16SJISTILDE」だと思われるが(未確認)、9iから実装されたものなので古いDBをそのまま使ってたりすると対処されてなかったりする。exp/impするのがめんどいとかたぶんそんな理由で。


で、このデータをMySQLに持ってくると、CP932とSJISがまじった状態になる。
別にUTF-8で扱ってる分には特にこの「〜」が元CP932なのかSJISなのかはそんな問題にならないんですが(Unicodeには2通りの文字がある。この辺はWave Dash問題として有名なので参照)、
アプリケーションやMySQLクライアントがSJIS(もしくはCP932)に変換して読みたい、ってなったときにどっちかが化けて困ることになる。


今回はたまたまOracleから中間ファイルに吐き出すところで置換ができたので
こんな感じで「〜」の文字コードをCP932の文字コードに統一した。

$r = preg_replace("/\xE3\x80\x9C/", "\xEF\xBD\x9E", $r);

このへんの仕組みさえわかってれば多分MySQL側にUPDATEパッチ当てるとかでもいけるはず。


ちなみにMySQL文字コード拾うのはこんな感じで

mysql> select HEX(ORD('〜'));
+-----------------+
| HEX(ORD('〜'))  |
+-----------------+
| E3809C          |
+-----------------+

ORD+HEXをつかう。この例の場合はmysqlクライアントの--default-charactor-setに依存した文字コードになる。