【PR】 家電品の安さ、品揃えに自信あり!
【PR】 ターボセルでセルライト対策
【PR】 中国語学習に役立つサイト
【PR】 ディズニーの英語システムなど英語リスニング教材比較
 

・CGIで特定の文字(表・予・申・能など)が文字化けする


自動バックアップ・テスサーバー付きの新機能スマートリリース

CGIやPHPなどの技術系でSuper FAQ(よくある質問)がこれです。下記のような文字化けが発生します。

文字化けしている漢字は「表」「予」「申」「能」「十」「ソ」などです。第1章の「Netscape4.Xのdocument.write時の文字化け」は音が「シ」のものに集中的に文字化けが見られるなど、顕著な規則性がありました。今回の文字化けは、「音」が似通っているという特色はありません。

しかし、それぞれの漢字のShift_JISコードを調べてみると、ある規則性が浮かび上がってきます。Shift_JISコードを調べるには、序論で紹介したようなIMEやことえりの文字一覧表でもいいのですが、ここではURLエンコードを利用してみます。

URLエンコードは、プログラマーでない方にはなじみがないでしょうが、実は日ごろ気がつかないうちに利用しているんです。検索エンジン(サーチエンジン)などで検索した時に、日本語などを検索キーワードとしてプログラムにGET方式で引き渡す際に、日本語のまま引数として渡すと具合が悪いので、日本語部分を16進数のコードに変換して、http://www.example.com/search.cgi?keywords=%82%A0%82%A2%82%A4%82%A6%82%A8などにアクセスする形でパラメータを渡しています。例えば、「%82%A0%82%A2%82%A4%82%A6%82%A8」とは「あいうえお」のURLエンコードした結果となります。あ = %82%A0」(1バイト目のコードが82、2バイト目のコードがA0)となります。

URLエンコードするには、サーバ上でCGI・PHPなどで変換することもできますが、ここではFlashを利用してみます。下記の駄作フォームに文字を入力し、URLエンコードというボタンをクリックするとエンコードできます。ただし、これはあくまでもShift_JISでの話です。ちなみに、Javascriptでもescape関数というのがありますが、確かにネットスケープではURLエンコードできますが、IEではunicodeに変換する関数になっています。互換性がありません。そこで、ローカルで動くもので、かつIEでもネットスケープでも動くものをということで、Flashで作成してみました。Flashは専門でいないので、思いっきり不細工ですがご了承ください。



全角カタカナの「シ」であれば、「%83V」と表示されると思います。この形式だと2バイト文字でないように一瞬見えますが、2バイト目の文字が16進で0x7F(「0x」とは以下に続く文字列が16進数であることを示す符号)以下の場合は、その16進値が示すASCII文字がアルファベットであれば、そのアルファベットのまま表記することができます。ですから、この場合、「V」とは「0x56」ですから、「シ」の文字コードは「0x8356」であることが分かります。

上記の文字化けサンプルに使われていた文字コードを左のFlashフォームを利用して調べてみると、下記のようなShift_JISコードであることが分かります。

漢字
コード95 5C97 5C94 5C90 5C83 5C
漢字
コード8F 5C8D 5C96 5C8C 5C93 5C

これだけ並べてみると、何か規則性は見えてこないでしょうか? そう、2バイト目が「5C」というコードになっています。「5C」を1バイト文字だと考えると、「\」です。「\」はプログラム内では特別な意味を持つことは、プログラムをされている方なら誰もがご存知だと思います。メタ文字と言われ、エスケープ処理をされる場合に使います。

ここでエスケープ処理の復習をしてみましょう。例えば、「私の名前は"田中太郎"です。」とprintしたいとします。この場合に、シングルクォーテーションを使うのも手ですが、もしダブルクォーテーションを使う場合に、print文の終わり(printされる目的語の範囲ともいうべきもの)が分からなくなってしまいます(結果的には、parse errorになります)。そこで、「print "私の名前は\"田中太郎\"です。";」とします。これにより、「田中太郎」をはさむダブルクォーテーションは、決してprint文の終わりを示す符号ではなく、単なる文字ですよとなります。

では、「print "私の名前は田中太\郎です。";」とすると、どう表示されるでしょうか? ここでは、「\マーク」は無視され、「私の名前は田中太郎です。」と出力されます。エスケープが必要のない文字の前に「\マーク」をつけても無視されるわけです。では、「\マーク」自体を出力したい場合はどうするかといいますと、「print "このりんごの値段は\\100です。";」とします。「\(円マーク)」の前に、メタ文字としての「\」をつけて、後ろの文字の「\」は文字通りの「\」だよという命令を出します。

ここまでが文字化け発生メカニズム解析のための下準備です。例えば、「可能性」という単語がなぜ「可柏ォ」(「ォ」は本当は小さい「オ」の半角カタカナ)に化けるのかについて調べてみます。

89 C294 5C90 AB

ここで、下調べで分かったことを取り入れると、下記のような変形になる。化学反応式のように分離結合が起こる。5Cがメタ文字として無視される結果

可能性 = 89C2 + 945C + 90AB = 89C2 + (94 + 5C) + (90 + AB) = 89C2 + (94 + 90) + AB = 89C2 + 9490 + AB

ここで、「94 90」となる漢字がいかなる漢字か、右のフォームで調べてみる。「94 90」なる漢字はURLエンコード式に表記すれば「%94%90」である。これをエンコードの逆、すなわちデコードすれば良いことになります。上の入力欄に「%94%90」と半角で入力し、「URLデコード」ボタンをクリックすれば、「柏」とでる。「AB」は半角の「ォ」です。ここに「可柏ォ」が誕生します。

同様に、「申請書」がいかにして、「瑞ソ書」(実際にはソは半角カタカナ)に化けるのかを検証してみます。

90 5C90 BF8F 91

申請書 = 905C + 90BF + 8F91 = (90 + 5C) + (90 + BF) + 8F91 = (90 + 90) + BF + (8F + 91)

ここで、「90 90」なる漢字は、上のURLデコーダーで調べてみれば、「%90%90」→「瑞」と分かります。同様に「%BF」とは「ソ」の半角カタカナですから、やはり"合理的に"文字化けしていることが分かります。

では、これらの文字も化けないようにするのにはどうしたら良いのでしょうか? それは2バイト目にある「5C」というコードはメタ文字ではないですよと教えてあげればいいことになります。「\」が一つのために、「\」が消えてしまって、文字化けしてしまっていたのですから、もう一つ「\」をつけてあげれば良いことになります。すなわち、「print "表\示";」「print "予\定";」「print "可能\性"」としてあげれば良いのです。

ただ、一々文字化けする可能性のある漢字に対して、エスケープ処理をしないといけませんので、これは面倒です。そもそも、このように、「5C」というコードを2バイト目に持つ漢字は、どれぐらいあるのでしょうか? 調べてみると下記のように全部で40文字あります。

Ы

 の3文字は機種依存文字。Windowsではテキストでそのまま表示されますが、Macで表示するためには画像などに変換する必要があり、ここでは画像ファイルを用いています。機種依存文字については、別章を参照してください。

滅多に使わない漢字も多いですので、実質は10文字から15文字も覚えていれば実用上は事足りるかもしれません。しかしながら、こういうエスケープ処理を一切しないでもいい方法が二つあります。一つは、問題のある漢字を含む文字列をprintする際に、ダブルクォーテーションではなくシングルクォーテーションで囲んでprintすることです。つまり、「print '表現
';」などとします。ダブルクォーテーションで囲まれている場合、メタ文字である「5C = \」が有効になってしまっていましたので、文字化けが発生しえたわけですが、シングルクォーテーションで囲めば文字化けは発生しません。

(ただし、出力されるソースが綺麗に改行されるようにと、「print '表現
\n';」とすると、「\n」がそのまま出力されます。このような場合は、「print '表現
' . "\n";」などとしなければなりません。かなり面倒です。)

もう一つの方法は、文字コードをShift_JISではなく、EUC-JPで作成することです。EUC-JPの漢字にはメタ文字は、現れません。ですから、エスケープ処理が必要ありません。もちろん、i-modeサイトの構築時や、デザイナーとの仕事の連携の関係でどうしてもShift_JISで開発しなければならない場合もあるでしょうが、それ以外の時はEUC-JPでプログラムは作成したほうが絶対的に無難です。

特に正規表現を用いる必要がある場合は、Shift_JISでプログラミングすると考慮しなければならないことが多すぎて大変です。このホームページの目的は文字化けの原因究明ですので、正規表現についての考察はしませんが、これらの問題点についての説明は、

● Shift_JISテキストを正しく扱う
http://homepage1.nifty.com/nomenclator/perl/shiftjis.htm

● Perlメモ:日本語を扱う perl スクリプトは EUC-JP で書く
http://www.din.or.jp/~ohzaki/perl.htm#JP_EUC_JP
などが詳しいです。参照してください。

話を元に戻します。さらに文字の化け方を考察してみましょう。
冒頭で紹介した文字化けサンプルでも明らかなように、文字化けする漢字は決まっていても、その文字化けする漢字に続く文字によって、化け方は千差万別です。直後の文字だけが化ける場合もあれば、二文字後の文字まで化ける場合があります。どの組み合わせでどのように文字化けするかは左のフォームでシュミレーションできます。

また、HTMLタグの「<」が化けると、本来改行があるべきところが改行されなかったりなど(例:機構<br>→機・br>)、レイアウトが崩れる原因になります。例えば、それが<input type=hidden〜>で渡す情報であれば、hiddenの内容は送信されず誤作動の原因になります。したがって、単なる文字化けで済まされない可能性があります。


▼ PHPで「表\示」「能\力」などと表示される場合
他にもプログラムに影響を与える場合があります。例えば、PHPでは、Magic Quotes GPC(GPCとはGet Post Cookieの意味)という設定があります。この設定をonにすると、「'(シングル・クォテーション)」や「"(ダブル・クォテーション)」、「\」(「\」)を含む文字列があると、GetなりPostで受信した文字列に対して自動的にエスケープしてくれるというものです。例えば「John's name」を「John\'s name」と処理してくれるため、データベースなどに登録する際にエラーにならないというものです。一見すると非常に親切そうですが、実は厄介な問題を孕んでいます。この章で述べたとおり、Shift_JISでプログラミングすると、「5C = \」が現れる文字が存在します。このような場合、「十和田湖」「能力」「申し込み」「表示」などの文字列はPOSTなりGETで受信するたびに、「十\和田湖」「能\力」「申\し込み」「表\示」などのようにエスケープされていますから、そのまま表示してしまうと不細工極まりませんので、必ずstripslashesしなければなりません。

このMagic Quotes GPCの問題を考えても、Shift_JISでプログラミングするのは、文字コード全般の知識を有している場合やi-モードサイトの構築などでどうしても避けられない場合などを除いては避けるべきです。(もちろんMagic Quotes GPCをoffにすれば、この問題は発生しません。あなたがサーバの管理者でない場合も、.htaccessで設定可能です。「php_flag magic_quotes_gpc off」とすればOKです。データベース登録時には、addslashesなどの処理が必要です。)

次のページからは、機種依存文字について説明します。まずは、Windowsでは表示可能でもMacでは文字化けする文字について考察します。