携帯コンテンツのつくり方 II

 絵文字をいじくっている間にふと思いついた。「絵文字を顔文字に変換できたら面白いんじゃないか」と。件の匿名掲示板には ( ´∀`) というモナー(顔文字)が頻繁に使われる。これを携帯で表現しようとすると、記号文字を引っ張り出して入力。単語登録しないとわずらわしい。だが携帯の絵文字 わーい(嬉しい顔) で一発入力できれば非常にラクだ。名づけて「モナー変換」ってオイ、そのまんまやんけっ。

 思いつくとすぐにやってみたくなる性分。利用者の需要などお構いなしだ。早速投稿処理のサブルーチンを探し出し、以下のように組んでみる。

$FORM{'comment'} =~ s/秊/\( ´∀`\) /g;

i モードシミュレータで投稿してみると、( ´∀`) の部分が「…br>」のように改行タグを巻き込んで壮絶に文字化け。うむむ、スクリプトは EUC で書いているから、投稿されたシフトJISと交じり合って下位バイトと融合してしまうらしい。今度は一度 ASCII 文字列「mo_0」に置き換え、これを jcode.pl でコンバートした後に再度変換してみる。

$FORM{'comment'} =~ s/秊/mo_0/g;
....
&jcode::convert (\$FORM{'comment'},'euc');
$FORM{'comment'} =~ s/mo_0/\( ´∀`\) /g;

こうして今度は問題なく変換。やれやれと思って本サーバにアップした。

 しかし、早速試してくれた利用者が投稿した記事を見て愕然。

な、なンだこれはーッ!(小池一夫風

 実はサイトに「表示」させる際には 秊 というコード入力で問題ないのだが、実際の携帯から送信されるのはシフトJISのバイナリコードであることが判明。ていうかバイナリってそもそも何なのさ?(こんなレベルでいじってます)

 インターネット上で流れるテキストには ASCII と呼ばれる1バイト(8ビット)のいわゆる「英数文字+α(記号や半角カナなど)」があり、こちらは特殊な処理なしに流すことができる。しかし日本語のように漢字やら記号やらやたらに文字数が多い言語の場合、1バイトでは間に合わないため、2バイトを使って送信している。つまり、英数半角の2倍の情報量を使わないといけない。送信される時には16進数(00〜FF)の2桁+2桁(実際はさらに2進数で0と1の16桁になるわけだが)が区切りのない状態で送られるわけだ。最初に文字コードの情報をヘッダに記載して「これから2バイトの文字列を送るよ」と知らせてあげれば、サーバやクライアントマシンで流れてくる16進数の羅列を解釈して、ディスプレイ上に正しい文字として表示させられる仕組みだ(と思う)。

 つまり、これはどういうことかというと、わーい(嬉しい顔) の文字は「秊」という形で送信されるわけではなく、「F995」という4桁の16進数で送られていたのだ。これでは置換演算子でマッチするわけがない。

 いろいろとネットをさ迷い調べつくし、以下のように書き直して動くようになった。

$FORM{'comment'} =~ s/\xF9\x95/mo_0/g;

ここの \x というのは、次に来る文字が16進数のコードであることを示すお約束らしい。しかしここでまた問題発生。今度は au が動かない…

 なぜだろうと思ったら、au は 秊 という文字コードを受信するとiモードと互換表示するようになっているのだが、送信する場合はiモードとは全く別の16進数コードを送っていたのだ。したがって au の顔文字 喜 の場合 F649 となるのである。SoftBank だけ分岐させればいいと思ったのにぃ。ていうか au はシミュレータも用意してないし…ぶつぶつ。

 思ったよりも携帯各社の溝は深かったことを痛感し、これらを総合して記述したスクリプトは次のようになった。

if ($ENV{'HTTP_USER_AGENT'} =~ /^(?:DoCoMo)/) { # DoCoMo
    &jcode::h2z_sjis (\$FORM{'comment'});
    $FORM{'comment'} =~ s/\xF9\x95/mo_0/g;
}
elsif ($ENV{'HTTP_USER_AGENT'} =~ /(?:KDDI|UP)/i) { # au
    &jcode::h2z_sjis (\$FORM{'comment'});
    $FORM{'comment'} =~ s/\xF6\x49/mo_0/g;
}
elsif (($ENV{'HTTP_USER_AGENT'} =~ /(?:J-PHONE|Vodafone|MOT-|SoftBank)/i) || ($ENV{'HTTP_USER_AGENT'} =~ /emulator/i)) { # SoftBank
    &jcode::h2z_sjis (\$FORM{'comment'});
    $FORM{'comment'} =~ s/\$Gv+/mo_0/g;
}
&jcode::convert (\$FORM{'comment'},'euc');
$FORM{'comment'} =~ s/mo_0/\( ´∀`\) /g;

各キャリアごとに処理を分岐させ、PCの際には読み飛ばすようにして負荷を軽くした。変換前に jcode::h2z_sjis で半角から全角にしている記述があるが、これは絵文字と半角カナがかち合って文字化けを起こすため。最後に EUC に直して顔文字に変換。やれやれ。

 で、お気づきの人もいるだろうが「mo_0」と入力しても ( ´∀`) に変換される。まあ面倒だし裏ワザとして残しておくのも面白いだろう。それに他の絵文字については回避する手段を今のところ講じていない。これについては要研究というか放置中…

 またさらに1つ問題がある。SoftBank の場合、喜喜喜 と続けて絵文字を打つと

$Gv$Gv$Gv

とならず、

$Gvvv

という風に v だけが連続する仕様になっている。どうやらパケット量節約のための独自仕様らしい。そのため上記のスクリプトでは正規表現の + を入れて、v が連続しようが1コに統一してしまうという乱暴な処理をしている。とりあえず、掲示板の説明には「連打する時は間にスペース必須」とごまかし、これも放置中…

携帯コンテンツのつくり方


 日記にこういう小難しいことを書くのは「俺ってどう? こんなことやっててスゴイでしょ?」という自慢である。もちろん技術情報を提供しようという高尚な考え方の人もいると思うけど、私は違う。ハッキリ言って自己顕示欲の現れだ。だって自慢したいんだから仕方ない。自分のことをひけらかそうと思わないなら、そもそもサイトを作ろうなんて思わないだろうし。
 でも人によってはこういう情報が役に立つかもしれないので、今回はいつもより気合い入れて書いてみます。

 某掲示板を運営していて、携帯からの利用者の意見が来た。どうやらiモードからだとマトモに表示されていないらしい。元のスクリプトは CGI に直接アクセスすれば見られると説明にはあったが、私がケータイコンテンツというものに興味がないというか、むしろバカにしていたぐらいなので全く動作確認などしていなかった。iモード携帯は持っていない上、使っている au に関しては EZweb どころかやメールさえ滅多に使わない。したがってどんな表示になるのかも全く想像がつかない。
 困ったなぁと思いつつ、とりあえず DoCoMo の開発者用サイトで HTML の仕様書を見ようとしたら、そこに PC 上で携帯の表示をシミュレートする Windows 用のソフトがフリーで置いてあった。これはありがたい。

iモードHTMLシミュレータ
http://www.nttdocomo.co.jp/service/imode/make/content/html/tool/
iモードHTMLシミュレータII
http://www.nttdocomo.co.jp/service/imode/make/content/html/tool2/

  NTT謹製のiモードHTMLシミュレータには古いバージョンとIIがあったので両方入れてみた。IIの方は FOMA などの画面が大きく新しめの機種を再現しているらしい。起動して早速その掲示板にアクセスしてみると確かにこれはひどい。というかよくこんな画面でアクセスする気になるものだ。まあIIの方だとなんとか「見れる」程度なのだが、問題は古い方。こちらについては文字が完全に化けてしまって読むことすらできない。
 調べてみたら DoCoMo 携帯は基本的にシフトJISにしか対応しないという。この掲示板はアスキーアートなどで半角カタカナが多用されることを考えて、全ての文字コードを EUC に書き直していた。他にも色々スクリプトを書き直しているため、携帯表示用のスクリプトでことごとくエラーを吐いている。FOMA などの最新機種は EUC にも若干対応しているようで、何とか表示できていたようだ。旧機種は無視してとりあえず文字は「読めればいい」ので、まずはHTML表示のエラーを直す。匿名でも投稿を受け付けるようにしているため、名前が空欄になる部分を if〜else で「名無しさん」と埋めるように改造。これでとりあえず解決。

 と、思ってローカルの Apache のバーチャルアドレスから表示確認しようとしたら表示エラー。サーバが存在しないと出る。おかしいなと思ってiモードHTMLシミュレータIIの[ブラウザ]>[オプション設定]>[ネットワーク設定]を確認したところ「HOSTSファイルを参照する」のチェックが外れていた。ここにチェックを入れて内部参照できるようになって解決。私のようにローカルのCGIテスト環境でWindowsのHOSTS設定している人は要注意だろう。

  IIで正しく表示されることも確認できて、本サーバにアップ。試しに EZweb で初めてアクセスしてみると、むむう、こちらは完全に文字化けしてしまう。どうやら EZweb はシフトJISのみしか対応していないらしい。こうなると悔しいので、こうなったら文字コードも直すことを決意する。
 ただしログから何から全部をシフトJISに戻すのはハッキリ言って無茶な上に改造した意味がない。というか数パーセントの利用者のため、たかがケータイのためにPC上で正常に動いているものを全部作り変えるのは無茶だ。スクリプトをいろいろ解析した結果、jcode.pl で全角カナを半角カナに変換している関数を見つけた。なるほど、CGI でログをいじらずに表示だけを切り分けているようだ。ファイルの I/O は増えるが表示前にテキストを一気にコンバートすれば済みそうだ。

 いろいろいじっているうちに、このスクリプトの構造が分かってきた。 CGI ファイルにアクセスするとまず UserAgent で携帯か否かを判定している。UA の先頭に DoCoMo、KDDI、J-PHONE という文字列があれば携帯表示用のサブルーチンに進む。機種によって若干の表示調整を行っているようだが、すでにその分岐が DoCoMo 505i 以前のものと仕様が古く、正常に動作していないようだった。とりあえずそれらをコメントアウトして、

&jcode::convert(\$imode_html,'sjis');
&jcode::z2h_sjis(\$imode_html);

と、すでにサブルーチンで $imode_html に格納されている〜のタグとテキストを一気にシフトJISに変換し、カタカナも携帯表示用に半角カタカナへコンバート。そしてiモードの場合はHTTPヘッダ情報に受信ファイルのバイト数を示す「Content-length」が必須になるため

my $len = length $imode_html;

と、バイト数を調べ

print "Content-length: $len¥n";
print "Content-type: text/html¥n¥n";
print $imode_html;
exit;

と書き出す。意外にあっさりと変換されて表示もスムーズ。これで旧バージョンのiモードHTMLシミュレータだけでなく、EZweb からの表示もOKとなった。

 こうしていじりだすとだんだん携帯コンテンツをつくるのが面白くなってきた。いろいろと調べてみると携帯のダイヤルボタンでダイレクトにリンクさせる独自の「accesskey」というタグを発見。

<a href="index.html" accesskey="1">ホーム</a>

のように記述すると、携帯利用者には当たり前の事なのだろうけど、これで[1]のダイヤルボタンを押すだけでダイレクトに index.html にアクセス。いちいち上下キーでリンクを移動する手間を省くことができるらしい。さらに携帯の独自コードでダイヤルボタンの「絵文字」を表示できるようで、ユーザはこれを見て「ダイヤルボタンで直リンク」と判断するようだ。ダイヤルボタンの絵文字の 1 は 驪 というコードになるため、

<a href="index.html" accesskey="1">&#63879; ホーム</a>

のように記述。するとシミュレータで「1 ホーム」のように表示され、いかにもケータイサイトらしい作りになった。EZweb も同じ絵文字コードのため(厳密には「互換」であって、これが後に私を苦しめるのだが…)、試しに自分の au ケータイでブラウジングしてみるとちゃんと絵文字も表示され、ボタンでサクサク移動できてなかなかよろしい。恐らく以前よりも非常に軽快な操作になっただろう。

 2つのキャリアに対応できるようになると欲が出て、SoftBank 携帯への対応もやりたくなってきた。調べてみると SoftBank にもウェブコンテンツビューアというツールが用意されていた。

ウェブコンテンツビューア
http://developers.softbankmobile.co.jp/dp/tool_dl/web/tool.php

NTTのものより様々な機種/メーカーが選択できて機能も豊富。絵文字も入力ツールが始めからついている(iモードは別ツール)。そのため最初は慣れるのに戸惑った。
 使い勝手が分かって、いろいろな機種で現在の状態を確認すると、iモードやauでは表示されていたダイヤルボタンのところが「驪」とそのままコードが丸見えになってしまうことに気づいた。調べてみると J-PHONE 時代から絵文字コードが他キャリアと互換性がないとのこと。これは困った。だったらわざわざ絵文字を使うのはあきらめて「1.」と書いておけばいいのだろうが、それではせっかく組み込んだのにイヤだ。

 色々調べてみたところ、どうやらバイナリコードをそのまま埋め込めば表示ができることを発見。[1]ボタンなら$F<と埋め込めばいいらしい。ただしそのまま CGI に埋め込むとエスケープ文字や特殊文字とかち合ってエラーを吐く文字もあるため、
$a_key1 = ‘$F<';

のようにシングルクォーテーションでくくって変数化。これで DoCoMo/au と SoftBank を UA で判定して分岐させ、$a_key1 を入れ替えて表示させればOK…と思いきやまた問題。全く設定した文字が表示されない。分岐が反映されないのだ。どうもおかしいと思って、ウェブコンテンツビューアの「HTTPログ」を確認すると

User-Agent: J-EMULATOR/4.0/J-K51/SN12345678901 KW/ Profile/...

などと表示されている。UA で J-PHONE、Vodafone、MOT-、SoftBank と先頭につくものを SoftBank ケータイと振り分けていたのだが、どうやらウェブコンテンツビューアの UA は J-EMULATOR やら Semulator などワケのわからんものになっている。仕方ないため、以下のようにして分岐させてみる。

if ($ENV{'HTTP_USER_AGENT'} =~ /^(?:DoCoMo|KDDI|UP)/) {
    $a_key1 = qq|&#63879;|;
    $a_key2 = qq|&#63880;|;
    $a_key3 = qq|&#63881;|;
.....
}
elsif (($ENV{'HTTP_USER_AGENT'} =~ /(?:J-PHONE|Vodafone|MOT-|SoftBank)/i) || ($ENV{'HTTP_USER_AGENT'} =~ /emulator/i)) {
    $a_key1 = '$F<';
    $a_key2 = '$F=';
    $a_key3 = '$F>';
    .....
}

乱暴ではあるが大文字小文字関係なく「emulator」とマッチすれば無理矢理 SoftBank ケータイということにしておく。まあ他のブラウザでヒットしたとしても、ロクなものではないだろうし…

 そんなこんなでようやくそれっぽくなってきたのだが…まだまだ格闘の日々は続くのであった(って続くの?)。