CGIぽん
モバイルCGI研究(i-mode編)
2002年9月13日更新

i-mode 絵文字の処理


目次


はじめに

ケータイ対応の CGI プログラムを組むときに頭を悩ませるのが i-mode 絵文字の処理。このページでは i-mode 絵文字の仕組みについて説明し、 i-mode 絵文字が入力された場合の処理方法と、 i-mode 絵文字の出力方法の例を紹介します。

i-mode 絵文字は i-mode のサービス開始以来、徐々にその種類を増やしており、今後ここに書いてある処理方法では間に合わなくなることも考えられますので、ご注意下さい。


i-mode 絵文字の正体

i-mode 絵文字は Shift_JIS コード 2 バイトで、第一バイトが "0xF8" または "0xF9" の領域に充てられています。("0x" は「16 進数ですよ」という印。)

第一バイト 第二バイト
F8 9F〜FC
F9 40〜53、55〜57、5B〜7E、80〜FC

i-mode 絵文字はパソコンや他のケータイ端末からもそのままでは見ることができませんので、 i-mode から絵文字の入力があったらそれをうまく除去・変換して他のブラウザでも問題なく見られるようにしなければなりません。


まちがった除去方法

i-mode 絵文字は第一バイトが "F8" または "F9" で始まっており、 Shift_JIS コードでは他にそのような文字はありませんので、次のような除去方法がすぐに考えられます。

Perl
# まちがった例
$str =~ s/[\xF8\xF9].//g;

しかしこの変換は次のような誤作動を起こします。

8DF9 9263 8E71 82CD 88F9 82DF 82C8 82A2

          ↓

8D63 8E71 82CD 88DF 82C8 82A2

この例文の場合はほどほどに(^^;)うまくいっていますが、 "F8","F9" で終わる文字の後ろにある HTML タグなどが一緒に除去されることもあるので結構深刻な問題です。

このような変換ミスをなくすため、文字列の先頭から一文字ずつチェックしていく必要があります。


絵文字除去サブルーチン

i-mode 絵文字を除去するサブルーチンは次のようになります。

Perl
# i-mode絵文字除去サブルーチン
# 2002.09.13 更新
# Shift_JISから他の文字コードに変換する前に使用すること

sub remove_emoji {

  my $old = $_[0];  # 変換前の文字列
  my $new = '';     # 変換後の文字列

  if ($old =~ /[\xF8\xF9]/) {
    # 絵文字を含む可能性があるときだけ処理をします
    while (1) {
      if ($old =~ s/^([\xF8\xF9][\x40-\x7E\x80-\xFC])+//) {
        # 先頭に絵文字列があった場合
        next;
      } elsif ($old =~ s/^([\x81-\x9F\xE0-\xF7\xFA-\xFC][\x40-\x7E\x80-\xFC])+//) {
        # 先頭に絵文字以外の Shift_JIS 2バイト文字列があったら
        # それを $new の後ろに連結
        $new .= $&;
      } elsif ($old =~ s/^.//) {
        # 先頭が ASCII または 半角カナ 1バイトの場合
        $new .= $&;
      } else {
        # $old がなくなったのでループを抜けます
        last;
      }
    }
  } else {
    # 絵文字はないのでそのまま代入
    $new = $old;
  }
  return $new;

}

# 呼び出し方の例
$str = &remove_emoji($str);

また、これと同等の変換をパターンマッチ(置換文)で書くと次のようになります。

Perl
# i-mode絵文字除去置換文
# 2002.09.13 更新
# Shift_JISから他の文字コードに変換する前に使用すること

$sjis  = '[\x81-\x9F\xE0-\xF7\xFA-\xFC][\x40-\x7E\x80-\xFC]|[\x00-\x7F]|[\xA1-\xDF]';
$emoji = '[\xF8\xF9][\x40-\x7E\x80-\xFC]';
$str =~ s/\G((?:$sjis)*)(?:$emoji)/$1/go;

絵文字変換サブルーチン

i-mode 絵文字を "&#数字;" (10進数表記)に変換するサブルーチンは次のようになります。この形式に変換しておけば、 jcode.pl などで文字コード変換を行なっても文字化けを起こすことはありません。

Perl
# i-mode絵文字変換サブルーチン
# 2002.09.13 更新
# Shift_JISから他の文字コードに変換する前に使用すること

sub replace_emoji {

  my $old = $_[0];  # 変換前の文字列
  my $new = '';     # 変換後の文字列

  if ($old =~ /[\xF8\xF9]/) {
    # 絵文字を含む可能性があるときだけ処理をします
    while (1) {
      if ($old =~ s/^[\xF8\xF9][\x40-\x7E\x80-\xFC]//) {
        # 先頭に絵文字列があった場合
        # 10進数表記にして $new に連結
        $new .= '&#' . unpack('n', $&) . ';';
      } elsif ($old =~ s/^([\x81-\x9F\xE0-\xF7\xFA-\xFC][\x40-\x7E\x80-\xFC])+//) {
        # 先頭に絵文字以外の Shift_JIS 2バイト文字列があったら
        # それを $new の後ろに連結
        $new .= $&;
      } elsif ($old =~ s/^.//) {
        # 先頭が ASCII または 半角カナ 1バイトの場合
        $new .= $&;
      } else {
        # $old がなくなったのでループを抜けます
        last;
      }
    }
  } else {
    # 絵文字はないのでそのまま代入
    $new = $old;
  }
  return $new;

}

# 呼び出し方の例
$str = &replace_emoji($str);

また、これと同等の変換をパターンマッチ(置換文)で書くと次のようになります。

Perl
# i-mode絵文字変換置換文
# 2002.09.13 更新
# Shift_JISから他の文字コードに変換する前に使用すること

$sjis  = '[\x81-\x9F\xE0-\xF7\xFA-\xFC][\x40-\x7E\x80-\xFC]|[\x00-\x7F]|[\xA1-\xDF]';
$emoji = '[\xF8\xF9][\x40-\x7E\x80-\xFC]';
$str =~ s/\G((?:$sjis)*)($emoji)/$1.'&#'.unpack('n',$2).';'/ego;

絵文字の出力法

i-mode 絵文字を出力する際は DoCoMo のi-mode 対応絵文字に書かれているとおり、"" といった 10 進数表記で書いておくのが一番簡単です。(i-mode対応HTML4.0に対応した機種に対しては各絵文字に対応したユニコードを "&#xE63E" というように HTML の中にテキストで埋め込むことも可能です。)

ただ、少しでもパケット料金を節約したい、という事であれば、これを 2 バイト文字に置き換えて出力しましょう。これを実現する最も簡単な方法は、次のような HTML ファイルをブラウザで開き、表示されている中黒点 "" をコピー&ペーストすることです。(ペーストの際に高機能なテキストエディタを使用するとうまくいかないことがあります。)

HTML
<html><body>&#63647;</body></html>

しかし i-mode 用の HTML ファイルに書く場合はこの方法で問題ないのですが、 Perl のソースコードに直接中黒点 "" を書き込むと、エラーを起こす場合があるようです。 このような場合には、"\xF8\x9F" のように直接 16 進コードを書いて出力するのが安全です。

Perl
# 16進コード直接表記による出力
# "&#63647;" を出力したい場合
# (10進数の 63647 は 16進数では F89F)

print "\xF8\x9F";

10 進数表記の絵文字を 2 バイトの 16 進コードに変換して出力する方法もあります。次のスクリプトを参考にしてください。

Perl
# "&#数字;"(10進数表記)形式から2バイトShift_JIS形式への変換

$fine = '63647'; # 10進数で表された絵文字コード
$fine = pack('n',$fine);
print "$fine";
Perl
# "&#数字;"(10進数表記)形式を含む文字列を変換
# "&#" と ";" の間の部分が "63" から始まる5桁の数字の場合にi-mode絵文字とみなす

$str =~ s/&#(63\d{3});/pack('n',$1)/eg;

各々の画像ファイルを用意してある場合には、"&#数字;"(10進数表記)形式を次のように変換するとよいでしょう。

Perl
# i-mode絵文字画像の出力

$imgdir = './emoji/';
$str =~ s/&#(63\d{3});/<img src="$imgdir$1.gif">/g;

jcode.pl と i-mode 絵文字

i-mode 絵文字を含んだ文字列を他のコードに変換するには、絵文字変換サブルーチンの項で述べたように、変換する前に、 i-mode 絵文字を "&#数字;" の形式に変える必要があります。

jcode.pl(v2.13) の中では Shift_JIS の正規表現が [\x81-\9F\E0-\FC][\40-\7E\80-\FC] となっていますが、第一バイトが "0xF0" より大きい文字は Shift_JIS の拡張領域にあたり、 JIS や EUC-JP にはこれに該当する文字はありません(このへんちょっとよくわかっていませんが…)。このため、第一バイトが "0xF0" 以降である i-mode 絵文字は誤変換されてしまい、文字化けの原因になります。また、この変換は不可逆なため、一度 Shift_JIS から他のコードに変換してから Shift_JIS に戻そうとしても、 i-mode 絵文字以降の文字が正しく変換されない可能性が高いです。


PHP と i-mode 絵文字

PHP4 の mb_convert_encoding() を使った場合は i-mode 絵文字は下駄コード "〓" に変身するだけで、文字列全体の文字化けを引き起こすことはないようです。


関連リンク

公式サイト
i-mode 対応絵文字
Perl 関連
Perlメモ
jcode.pl official page
CGIぽん
モバイルCGI研究(ドットi編)ドットi絵文字の処理
モバイルCGI研究(J-SKY編)J-SKY絵文字の処理

このページの履歴

2002.09.13
除去・変換の例を更新。何ヶ所かタイプミスをしていました。ご迷惑をおかけしました。
絵文字の範囲を最新のものに更新。ユニコード表記に関する文章を追加。
2002.03.18
除去・変換の例を更新。半角カナの存在を見落としていました。
jcode.pl と PHP について言及。
2001.12.29
除去・変換の例の /x 修飾子を間違って記述していたのを修正。ご迷惑をおかけしました。
2001.11.01
除去・変換の例の間違いを修正。ご迷惑をおかけしました。
2001.10.08
出力方法についての説明を追加。
除去・変換の例を更新。
2001.06.22
除去・変換の例に置換文を使った方法を追加。
2001.06.03
除去・変換の例を更新。
2001.05.02
ページリニューアル。
2001.01.19
除去・変換の例の間違いを修正。
2000.12.29
初版。

CGIぽんはINFOJAMの安価で快適なサーバを利用しています。