最近、某とあるサイトにて文字化けが起こるという申告が増えてきました。
理由は2つあって、ひとつは携帯(AU)からのアクセスが増えてきたということ、もうひとつは海外からの利用が増えてきたことです。
いちおう明示的にUTF-8と指定しているのですが、AUさんはShift-JISで送ってきたり、中国語のよく分からないクライアントはGB2312で送ってきたりして、文字判別は一筋縄にはいきません。
今回は、文字化けを防ぐ改造に当たってやったことを書きたいと思います。
さて文字コードを自動判別するという技術は色々あって、よく利用されるのは特定の文字コードでしか利用されない文字を探し当て、判別するというものです。
言わずと知れた漢字コード変換コマンドである nkf でも、上記の方法で非常に精度の高い自動判別を行ってくれます。
ただ、これには大きな問題があります。それは「短い文字列だと判別できない」ということです。
某とあるサイトでは、単語を入力してロゴを作るというウェブサービスを提供していますが、入力される文字列が短いことからうまく自動判別がうまくいきません。
そのため今回取った対策は、submitされるフォームデータと共に判別用文字列を送るというものです。
例えば「文字」という文字列をUTF-8で送ると%E6%96%87%E5%AD%97となり、Shift-JISで送ると%95%B6%8E%9Aとなります。
ですので、判別用文字列がどのような文字列になって送られてきたかを見ると、他のフォームデータの文字コードを知ることが出来るというわけです。
それでは実際のプログラムです。
detectstrのところが、判別用文字列を埋め込んだところです。
ちなみに「文字」というのを利用した理由ですが、日本語のみならず、中国語簡体字、中国語繁体字においても同じ形だからということです。例えば自動という文字の場合は、簡体字で動という文字が異なるコードになってしまいますので利用できません。
なお最初は「日本」という文字を判別用文字列にしていたのですが、hiddenで日本という文字列が入っていると中国の利用者の方からいらぬ誤解を受けたり都市伝説化するのも嫌なので、途中で変えました。
送信側プログラム
<form action="test.php">
<input type="hidden" name="detectstr" value="文字" />
名前: <input type="text" name="name" />
<input type="submit" value="送信" />
</form>
受信側プログラム
<?php
$charsetList = array(
"utf-8" => "%E6%96%87%E5%AD%97",
"sjis" => "%95%B6%8E%9A",
"euc-jp" => "%CA%B8%BB%FA",
"jis" => "%1B%24BJ8%3Bz%1B%28B",
"Big-5" => "%A4%E5%A6r",
"HZ" => "%7E%7BNDWV%7E%7D",
"EUC-KR" => "%D9%FE%ED%AE",
"GB2312" => "%CE%C4%D7%D6",
);
$charset = NULL;
foreach( $charsetList as $key => $val ){
if( @$_GET["detectstr"] == $val ){
$charset = $key;
break;
}
}
if( !$charset ) die("Couldn't detect a character set");
$name = mb_convert_encoding( @$_GET["name"], "UTF-8", $charset );
print htmlspecialchars($name);
以上、いかがでしたでしょうか。
少々セコいですが、簡単かつ確実に行える対策です。
ただ、フォームの送信元と送信先の両方のプログラムに手を入れることになりますので、それが出来ない環境では別の方法をとる必要がありますのでご注意を。