encodeURIComponent()を使ってAjaxで送信した日本語文字(〜など)が,サーバ側で文字化けする場合への対処. ブラウザからサーバに日本語文字列を送信する時の話だが, 普通にHTMLでサブミットする場合は,ブラウザが勝手にそのページの文字コードかつURLエンコーディングした結果を送信してくれるので, 特に何も考えなくて良いのだが,Ajaxの場合はエンコードを自前でやらねばならない. この場合,JavaScriptのencodeURIComponent()関数を使うのが一般的のようだ. これを使うと,
の両方を行ってくれる. サーバ側では,受信した内容をURLデコードして, さらに必ずUTF-8で来ているはずなので,その前提で必要な文字コードへ簡単に変換して使うことができる. ...のはずだったのだが,文字化けが生じてはまってしまった. このページでは,その顛末を書きます. なお,Windowsのブラウザで発生した現象で,ページのエンコーディングはEUC-JPです.
現象文字「〜」を,Windowsブラウザ上のJavaScriptのencodeURIComponent()でUTF-8に変換し, それをサーバに送信したが,サーバ側でPerlのEncode.pmを使ってデコードできなかった.
ブラウザでの変換ブラウザのアドレスバーに,javascript:alert(encodeURIComponent('〜')) と入力すれば判るように,encodeURIComponent()では以下の変換結果が得られる.
これは,いわゆるMicrosoft仕様の変換だ.(MS932)
サーバでの変換PerlのEncode.pm,Jcode.pmで「euc-jp」を使った場合, UTF-8からEUC-JPへの変換には,以下のようにJIS X 0221準拠の変換が適用される.
そのため,ブラウザから「%ef%bd%9e」として送信されてきた文字は, 本来の「〜」に変換されずに文字化けとなってしまう.
対策理屈の上では,以下の3案が考えられる
このうち,1.はどうすればよいのかさっぱり判らないので却下. 3.については,こういうものがあるらしい
が,インストールするのが面倒なのでやめた. ということで,2.のコード置換をやることにした.具体的には,「〜」のマッピングは以下になっているので,
Encode.pmのeuc-jpでデコードするためには,「ef bd 9e」→「e3 80 9c」に変換すればよい.
変換の具体的な内容昔,JavaでWindows-31Jが使えなかった時代によく見ていたページ
を参照しつつ,実際の動きを調べたところ,Encode.pmで文字化けせずに変換するためには以下のコード置換が必要だとわかった. $a =~ s/%ef%bd%9e/%e3%80%9c/gi; # 〜 U+FF5E(FULLWIDTH TILDE) → U+301C(WAVE DASH) $a =~ s/%e2%88%a5/%e2%80%96/gi; # ‖ U+2225(PARALLEL TO) → U+2016(DOUBLE VERTICAL LINE) $a =~ s/%ef%bc%8d/%e2%88%92/gi; # − U+FF0D(FULLWIDTH HYPHEN-MINUS) → U+2212(MINUS SIGN) $a =~ s/%ef%bf%a0/%c2%a2/gi; # ¢ U+FFE0(FULLWIDTH CENT SIGN) → U+00A2(CENT SIGN) $a =~ s/%ef%bf%a1/%c2%a3/gi; # £ U+FFE1(FULLWIDTH POUND SIGN) → U+00A3(POUND SIGN) $a =~ s/%ef%bf%a2/%c2%ac/gi; # ¬ U+FFE2(FULLWIDTH NOT SIGN) → U+00AC(NOT SIGN) この作業をやるために,UTF-8とUTF-16を相互変換するJavaScriptを書く羽目になった. なお,上記の6文字とたいていセットになってくる ― U+2015(HORIZONTAL BAR) については,encodeURIComponent()でJIS準拠の変換が行われていたので,特に変換する必要はなかった.
雑感JavaでWindows-31Jが使えるようになってから,この問題のことはすっかり忘れていたが, UnicodeとWindowsを使う限り,逃れることはできないのか...そういえば,Macのブラウザだとどうなるのかが判らないが, まぁこの変換を入れたところで,副作用は出ないだろう(多分).
参考資料
|