http://tanaka.sakura.ad.jp さくらインターネット創業日記: 2010年12月アーカイブ

2010年12月アーカイブ

最近、某とあるサイトにて文字化けが起こるという申告が増えてきました。
理由は2つあって、ひとつは携帯(AU)からのアクセスが増えてきたということ、もうひとつは海外からの利用が増えてきたことです。
いちおう明示的にUTF-8と指定しているのですが、AUさんはShift-JISで送ってきたり、中国語のよく分からないクライアントはGB2312で送ってきたりして、文字判別は一筋縄にはいきません。
今回は、文字化けを防ぐ改造に当たってやったことを書きたいと思います。

文字化けの話はGoogleででも検索を

さて文字コードを自動判別するという技術は色々あって、よく利用されるのは特定の文字コードでしか利用されない文字を探し当て、判別するというものです。
言わずと知れた漢字コード変換コマンドである 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);


なお、「文字」以外を判別用文字列に利用したい方の為に、コードジェネレータを作りました。


以上、いかがでしたでしょうか。
少々セコいですが、簡単かつ確実に行える対策です。
ただ、フォームの送信元と送信先の両方のプログラムに手を入れることになりますので、それが出来ない環境では別の方法をとる必要がありますのでご注意を。

知り合いと話していた際に、「ddコマンドでディスクをコピーしているとき、scpのようにプログレスバーが表示できたらいいのに」という話を聞きました。
意外と知られていないのですが、USR1シグナルを送ることで途中経過を表示させることが可能です。

[root@wwwxxxxu ~]# dd if=/dev/zero of=/dev/null &
[1] 31092
[root@wwwxxxxu ~]# kill -USR1 31092
7764899+0 records in
7764898+0 records out
3975627776 bytes (4.0 GB) copied, 6.02001 seconds, 660 MB/s
[root@wwwxxxxu ~]# kill -USR1 31092
25123042+0 records in
25123041+0 records out
12862996992 bytes (13 GB) copied, 17.8137 seconds, 722 MB/s
[root@wwwxxxxu ~]# kill -USR1 31092
43986419+0 records in
43986418+0 records out
22521046016 bytes (23 GB) copied, 31.3739 seconds, 718 MB/s
[root@wwwxxxxu ~]# kill 31092
[1]+ Terminated dd if=/dev/zero of=/dev/null
[root@wwwxxxxu ~]#

ddを利用する際は大きなファイルのことが多いので、途中で経過が見えるだけでもストレスが減るのではないでしょうか。
ちなみに私は、上記の出力を元にプログレスバーを表示するスクリプトを作りました。

自己紹介

本名:田中邦裕/1978年生まれ
1996年にさくらインターネットを創業しホスティングサービスを開始。 98年に有限会社インフォレスト(2000年に解散)設立後、翌年にさくらインターネット株式会社を設立して社長に就任。
05年に東証マザーズに上場
kunihirotanakaをフォローしましょう

このアーカイブについて

このページには、2010年12月に書かれたブログ記事が新しい順に公開されています。

前のアーカイブは2010年11月です。

次のアーカイブは2011年1月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

ウェブページ

Powered by Movable Type 5.04