Oracleの波ダッシュ(〜)問題に対処する
まず、UTF-8とSJISで文字コードを変換したときに化けやすい文字、ってのがある。
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
でさらに、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> select HEX(ORD('〜')); +-----------------+ | HEX(ORD('〜')) | +-----------------+ | E3809C | +-----------------+
ORD+HEXをつかう。この例の場合はmysqlクライアントの--default-charactor-setに依存した文字コードになる。