.NET TIPS

[ASP.NET]携帯サイトで絵文字を自動変換するには?

デジタルアドバンテージ 岸本 真二郎
2008/05/01

携帯サイトを作成するときに起きること

 携帯電話向けのWebページを作成する場合、どうしても携帯電話のキャリア(DoCoMoやAUなど)を考慮したページ作成が要求される。キャリアごとに最適に表示されるような凝ったページを作る場合は、それぞれのキャリアごとにページを作成しなければならない。それほど凝ったデザインが要求されない場合には、各キャリアに共通するページを作成することができるが、キャリアごとのHTML(XHTML)の仕様の詳細が異なったり、エンコードの指定が異なったりで、なかなか大変である。

 しかも、携帯サイトには付きものの絵文字は、キャリアによって記述方法が異なっており、絵文字を含むHTMLファイルをすべてのキャリアで同じように表示させるのは難しい。ここでは、3つのキャリア(DoCoMo、AU、SoftBank)に共通のHTMLファイルを作成しIISで公開する場合に利用できる、絵文字の自動変換の一例を紹介しよう。

やり方は1つではないが

 絵文字の自動変換については「連載:.NETでモバイル・サイト開発を始めよう 第2回 携帯サイトの『ドコモ、au、SoftBank』への対応」でも紹介されている。そちらではカスタム・コントロールを用いてキャリアごとの絵文字を携帯電話に返すようにしている。これも有効な方法だが、ボリューム(ページ数)がある携帯サイトの場合、1つ1つのページをVisual Studioで作成するのは大変であるため、コンテンツ(ページ)の作成にはWebオーサリング・ツールを使う場合が多い。あるいは、携帯サイト向けのページはサイズの制限がある(あまり大きなサイズのページは携帯電話で読み込めない可能性がある)ため、テキスト・エディタで作成してしまうことも多い。

今回の条件

 本記事では、次に示す条件の下で作成する携帯サイト向けのページに含まれる絵文字を自動変換する処理を考える。

(A)DoCoMoで利用可能な標準的な絵文字(基本絵文字)のみを使用する

(B)ページのエンコードはShift-JIS

(C)Webサイト全体をASP.NETで構築(コンテンツは.htmlファイルではなく.aspxファイル)

 (A)については、今回対応するキャリアで共通の絵文字を扱う必要があり、DoCoMoが提供している「基本絵文字」を対象とすることにする。基本絵文字であれば、AUやSoftBankにも同じデザインの絵文字が用意されているので、変換が可能である。

 (B)は絵文字を変換するためというよりも、キャリア共通のHTMLファイルを作成するために必要な条件と考えた方がよい。携帯電話用のWebページを作成する場合、1つのキャリアでも、機種により対応するマークアップ言語(HTML/XHTML/HDMLなど)のバージョンが異なるので、すべてのキャリアのすべての機種で表示可能なページを作成するのはかなり難しい。エンコーディングはShift-JISもしくはUTF-8が現実的なのだが、ここではShift-JISを用いたページを想定している。

 (C)については、静的に作成したコンテンツ・ファイルに対して自動変換処理の挿入が必要であるため、拡張子を.htmlではなく、.aspxにしてASP.NETが利用できるようにしておく。当然ながらWeb サーバはIISを前提にしている。ASP.NETを用いないで、サーバ側で独自の処理を挿入する場合は、ISAPIフィルタの作成を検討しなければならず、プログラミング言語(C++)の選択やISAPIに関連する予備知識の収集を考えると、これはスマートな実装とは思えないだろう。

自動変換の方式

 今回利用する自動変換を行う携帯電話向けページは、DoCoMoの絵文字(Shift-JIS)を「&#xxxxx;」という形式(xxxxxには数値が入る)の数値文字参照で記述する。ただしこれはDoCoMoが推奨する記述方法ではない。DoCoMoの携帯コンテンツ作成をサポートするサイトでの説明では、Shift-JISでコンテンツを作成する場合は、絵文字を表す2byteコード(バイナリ値)を直接埋め込むように指示している。そこで今回の実装では、ソース・コード(.aspx)上では数値文字参照で記述しておき、この数値文字参照を適宜キャリアごとの絵文字形式に変換することにする。

 例えば太陽を表示する絵文字は、DoCoMoのShift-JISコードでは、



で記述するが、DoCoMoの場合はこれをバイナリ値に変換して「\xF7EB」(63647は16進数ではF7EB)を埋め込む。

 SoftBankではDoCoMoの絵文字に対応する「#Gj」という、SoftBankが定義するWebコードと呼ばれるコードに変換する。SoftBankでは上記のWebコードを埋め込む際にシフトイン(SI:0x1B)コードとシフトアウト(SO:0x0F)のコードを入れなければならず、結果的に

SI + #Gj + SO

というコードに変換しなければならない。

 またAUの場合は、対応する絵文字番号は「44」である。本来ならAUでは絵文字は次のような<img>タグで記述する。

<img localsrc="44"></img>

 しかし幸いなことに、AUの場合は、AUのサーバでDoCoMoの絵文字を対応する絵文字に変換してくれる。要するにAUの場合は、基本絵文字を使う限りにおいてはDoCoMoと同じ記述を行って大丈夫ということだ。

 結論としては、DoCoMoとAUの場合は、数値文字参照をバイナリに変換し、SoftBankの場合は、SI、SOコードを含んだWebコードに変換すればよいことになる。

変換テーブルの用意

 非常に前置きが長くなったが、要するに携帯電話に送るHTMLファイル内から「&#xxxxx;」を見つけて、各キャリアに該当するコードに置換すればよいだけである。

 DoCoMoが定義している基本絵文字は176種類であるので、176件のデータを含む変換テーブルを用意しておく必要がある。

public Dictionary<uint, string> cnvDocomo2Softbank;
cnvDocomo2Softbank = new Dictionary<uint, string>();
Docomo2Softbank.Add( 0,    "G@" );
Docomo2Softbank.Add(63647, "$Gj");
Docomo2Softbank.Add(63648, "$Gi");
……以下省略……
DoCoMoが定義している基本絵文字の変換テーブル

 この変換テーブル(cnvDocomo2Softbank)は、DoCoMoの絵文字のShift-JISコードと、SoftBankのWebコードをペアにしたDictionary型の変数を作成している。

HTML内の絵文字を変換するコード

 後は数値文字参照を見つけてテーブルを参照して置換していくのだが、プログラム自体のエンコーディング(Unicode)とHTMLファイルのエンコーディング(今回はShift-JIS)を考慮する必要がある。このため以下のような単純な置き換えではうまくいかない。

htmlString.Replace(&#63647;", "\xF7EB");

 これはUnicodeの文字列にShift-JISの文字を挿入してしまうことになるからだ。そのため、あらかじめメモリ・ストリームを別途用意しておき、そこに絵文字以外の部分と変換した絵文字のコードをセットするようにする。

public enum KetaiCarrier
{
  docomo,
  AU_KDDI,
  SoftBank
}

public string ConvertEmoji(string htmlSource)
{
  string sResult = "";

  using (MemoryStream memStrm = new MemoryStream())
  {
    Encoding encSjis = Encoding.GetEncoding(932); // shift-jis

    int iCopiedPos = 0;
    byte[] orgBytes;

    Regex rgx = new Regex(@"&#\d{4,5};");
    Match rgxMatch = rgx.Match(htmlSource);

    while (rgxMatch.Success) {

      // 絵文字が見つかった位置までメモリ・ストリームに書き出す
      int len = rgxMatch.Index - iCopiedPos;
      if (len > 0)
      {
         // Shift-JISでストリームに書き出す
          orgBytes = encSjis.GetBytes(htmlSource.Substring(iCopiedPos, len));
          memStrm.Write(orgBytes, 0, orgBytes.Length);
      }

      // 絵文字を変換
      uint emojiCode = 0;
      int emojiLen = 0;
      byte[] sjisEmoji = new byte[2];
      emojiCode = parseEmojiCode(rgxMatch.Value );

      switch (this.carrier) { // ターゲットとなるキャリア

          case KetaiCarrier.docomo: // DoCoMo
            sjisEmoji = getDocomo(emojiCode, out emojiLen);
            break;

          case KetaiCarrier.AU_KDDI: // AU-KDDI
            sjisEmoji = getAU(emojiCode, out emojiLen);
            break;

          case KetaiCarrier.SoftBank: // SoftBank
            sjisEmoji = getSoftBank(emojiCode, out emojiLen);
            break;

          default:
            sjisEmoji = getDocomo(emojiCode, out emojiLen);
            break;
      }
      memStrm.Write(sjisEmoji, 0, emojiLen);

      iCopiedPos = rgxMatch.Index + rgxMatch.Length;
      rgxMatch = rgxMatch.NextMatch();
    }

    // 絵文字以降をメモリ・ストリームに書き出す
    if (iCopiedPos < htmlSource.Length)
    {
      orgBytes = encSjis.GetBytes(htmlSource.Substring(iCopiedPos));
      memStrm.Write(orgBytes, 0, orgBytes.Length);
    }
    sResult = encSjis.GetString(memStrm.ToArray()); // 文字列に戻す
   }

   return sResult;
}
文字列に含まれる絵文字の記述を変換する

 ConvertEmojiメソッドでは、パラメータとしてHTMLファイルの内容を文字列として受け取り、絵文字を変換した後の文字列を戻り値として返す。

 メソッドの内部では、Shift-JIS文字列用にメモリ・ストリームを作成してから、正規表現を使って数値文字参照を見つける。数値文字参照が見つかったら、数値文字参照が現れるまでHTMLの内容をメモリ・ストリームにShift-JISで書き出す。そして上述した変換テーブルを使って絵文字に対応するコードをメモリ・ストリームに書き込む。KetaiCarrier列挙型を使ってキャリア別の処理を行っているが、これは事前に(コンストラクタなどで)どのキャリアから呼ばれたかを設定しておくものとする。これを数値文字参照が見つからなくなるまで繰り返す。すべて変換し終わったら、メモリ・ストリームの内容を文字列に戻す。

■parseEmojiCodeメソッド

 上記のConvertEmojiメソッドから呼び出しているparseEmojiCodeメソッドは、数値文字参照の中身を数値として取り出しているだけだ。DoCoMoの絵文字コード(Shift-JIS)を返す。

private uint parseEmojiCode(string sval)
{
  uint code;
  try {
    sval = sval.Replace(";", "");
    code = uint.Parse(sval.Substring(2));
  } catch {
    code = 0;
  }
  return code;
}
parseEmojiCodeメソッド

■getDoComo/getAU/getSoftBankメソッド

 各キャリア向けの絵文字のコードをバイト列として返すのがこれらのメソッドである。getAUメソッドはgetDoCoMoメソッドと同じなので省略する。

 getDoCoMoメソッドは、渡されたShift-JISの絵文字のコードをバイト列に変換しているだけだ。一方、getSoftBankメソッドは、上述した変換テーブルを使って、対応する絵文字コードに変換している。SoftBankの携帯電話ではSI、SOコードが必要になるので、5byteのバイト列を返している。

private uint defaultCode = 63647; // 変換できない場合の代替絵文字

private byte[]getDoComo(uint docomoCode, out int length)
{
  length = 2;
  byte[] webcode = new byte[length];
  if (docomoCode == 0) { // 絵文字コードが判別できない場合
    docomoCode = defaultCode
  }
  webcode[0] = (byte)(docomoCode >> 8);
  webcode[1] = (byte)(docomoCode);
  return webcode;
}

private byte[] getSoftBank(uint docomoCode, out int length)
{
  length = 5;
  byte[] webcode = new byte[length];
  string sWebCode;

  if (this.cnvDocomo2Softbank.ContainsKey(docomoCode)) {
    sWebCode = this.cnvDocomo2Softbank[docomoCode];
    char[] s = sWebCode.ToCharArray();
    webcode[0] = 0x1b; // SIコード
    webcode[1] = (byte)s[0];
    webcode[2] = (byte)s[1];
    webcode[3] = (byte)s[2];
    webcode[4] = 0x0f; // SOコード
  } else {
    webcode[0] = 0x1b;
    webcode[1] = (byte)'$';
    webcode[2] = (byte)'G';
    webcode[3] = (byte)'j';
    webcode[4] = 0x0f;
  }
  return webcode;
}
getDoComo/getAU/getSoftBankメソッド

 以上のコードは、1つのクラス(EmojiConvクラス)に納めて、絵文字変換を行うライブラリとしてプロジェクトを作成しておこう(Emoji.Conv.dll)。

絵文字変換を行うタイミング

 以上の作業で、DoCoMo向けに記述した絵文字を含むコンテンツを、各キャリアに対応した形に変換できるようになった。ここまでの作業はWebサーバとは無関係だったのだが、ここからはWebサーバ(IIS)側の処理となる。

 今回の場合では、Webサイトに含まれるすべてのWebページで絵文字の変換を行う必要があるため、「TIPS:[ASP.NET]Webページに含まれるリンクを動的に変更するには?」と同様にフィルタを設定し(HttpResponse.Filter)、そこで絵文字の変換を行うようにする。

 ただし、すべてのページについてコードビハインド ファイルを用意しPage_Loadメソッドを記述して……ということは行わず、コードビハインドを用いないコーディング・スタイルで.aspxファイルに直接コードを記述する。

<%@ Page language="c#" %>
<%@ Import Namespace="System" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.Text" %>
<script language="C#" runat="server">

string ua = "";
string carrier = "";

void Page_Load(Object sender, EventArgs e)
{
  // キャリアの判別
  ua = Request.UserAgent;
  if( ua.Contains( "DoCoMo" )) {
    carrier = "DoCoMo";
  } else if( ua.Contains( "KDDI" )) {
    carrier = "AU";
  } else if( ua.Contains( "SoftBank" )) {
    carrier = "SoftBank";
  } else if( ua.Contains( "J-PHONE" )) {
    carrier = "SoftBank";
  } else if( ua.Contains( "Vodafone" )) {
    carrier = "SoftBank";
  } else if( ua.Contains( "Vemulator" )) {
    carrier = "SoftBank";
  } else if( ua.Contains( "Semulator" )) {
    carrier = "SoftBank";
  } else {
    carrier = "Other";
  }
  // キャリア情報を渡す
  AffiliateFilter filter = new MyFilter(Response.Filter, carrier);
  Response.Filter = filter;
}
.aspxファイルの先頭に記述する内容
キャリアの判別と、Responseオブジェクトのフィルタを設定している。

 しかし、さすがにこのような記述を、サイト内にあるすべてのHTML(.aspx)ファイルに埋め込むのは大変なので、SSI(Server Side Include)を使うようにする。C#で記述したコードは、logic.incといった名前のファイルにしておき、これをインクルードする。これには、

<!--#include virtual="/logic.inc" -->

という記述をすべての.aspxファイルの先頭に記述しておく。

 フィルタの処理については上述の記事を参照していただきたいが、ポイントとしては、Response.Filterプロパティを独自のオブジェクトに置き換えると、IIS(ASP.NET)がブラウザ(ここでは携帯電話。厳密には携帯電話に直接返しているわけではないが)に返す処理(Writeメソッド)を独自に処理できるというものだ。

 Writeメソッドでは、ブラウザに送る内容が渡されるので、ここで、絵文字の変換を行う。なお、Writeメソッドで渡されるコンテンツはbyte型の配列なので、正規表現での文字列の検索を行うために、一度string型に変換する必要がある。

public class MyFilter : Stream
{
  private Stream stream;
  private StreamWriter streamWriter;
  private Decoder dec;
  private System.Text.Encoding enc;
  private string m_carrier;
  private mobileMisc.EmojiConv ecnv;

  // コンストラクタ
  public MyFilter(Stream stm, string carrier)
  {
    enc = System.Text.Encoding.GetEncoding("shift_jis");
    stream = stm;
    streamWriter = new StreamWriter(stream, enc);

    dec = enc.GetDecoder();
    m_carrier = carrier;
    ecnv = new mobileMisc.EmojiConv(carrier);
  }

  // ブラウザにコンテンツを書き出す
  public override void Write(byte[] buffer, int offset, int count)
  {
   // EmojiConv型の ecnv は初期化されている
    char[] temp;
    string str = new string(buffer, offset, count)
    str = ecnv.ConvertEmoji(str); // ここで絵文字を変換
    temp = str.ToCharArray();
    streamWriter.Write(temp, 0, temp.Length); // あらためてストリームに出力
    streamWriter.Flush();
  }
  public override int Read(byte[] buffer, int offset, int count) {
    throw new NotSupportedException();
  }

  public override bool CanRead { get { return false; } }
  public override bool CanSeek { get { return false; } }
  public override bool CanWrite { get { return true; } }
  public override long Length { get { throw new NotSupportedException(); } }

  public override long Position {
    get { throw new NotSupportedException(); }
    set { throw new NotSupportedException(); }
  }

  public override void Flush() { stream.Flush(); }

  public override long Seek(long offset, SeekOrigin origin) {
    throw new NotSupportedException();
  }

  public override void SetLength(long value) {
    throw new NotSupportedException();
  }
}
Streamクラスを継承したフィルタ・クラス

HTMLファイルの記述例

 先ほどのEmojiConvクラスを利用するには、利用するWebサイトにbinフォルダを作成して、そこに作成したライブラリをコピーしておく。こうするとASP.NETは自動的にライブラリを参照するようになる。

 以上の作業で、1つのHTML(.aspx)ファイルで3つのキャリアの絵文字に対応するWebページが作成できるようになった。ライブラリの作成やフィルタの用意が必要だが、これらさえ用意しておけば、後はテキスト・エディタでも携帯サイトのページが作成できる。これは次のように記述する。

<!--#include virtual="/logic.inc" -->
<html>
<body>
&#63721;<font color="#FF00FF">お手軽フリーコール</font>&#63721;
</body>
</html>
携帯電話用のHTML(.aspx)ファイルの例
数値文字参照で絵文字を記述する。

 今回はSoftBank向けの絵文字の変換テーブルを用意したが、絵文字に対応する画像を独自に用意して変換テーブルを作成すれば、通常のPC用のブラウザからアクセスした場合にも絵文字画像を表示できるようになる(携帯電話のエミュレータを使わなくても絵文字の確認が行えるようになる)。

注意点

 今回紹介した例はあくまで実装例なので、以下の点には考慮していないことを注意していただきたい。

(1)絵文字ではない数値文字参照の処理

(2)フィルタ処理(Writeメソッド)では1ページ分の内容が一度に送られるとは限らない

 特に(2)については、実際にWriteメソッドに渡される内容を確認した限りでは、一度にページ全体が渡されていたのだが、それを前提にするのはよくない。数値文字参照の記述が分断される形でWriteメソッドが複数回呼び出された場合、ここで紹介した処理では正しい絵文字の変換は行えない。

■Unicode(UTF-8)ならもっと楽になる

 旧機種への対応や、既存のHTMLファイルへの影響を考えなくてよいのであれば、対象機種を十分検討(限定)し、各キャリアのサーバで行われるXHTMLから携帯電話ごとの自動変換処理に任せることで、今後はUTF-8で記述するのが正しい選択となるかもしれない。エンコードを気にする必要がなくなれば、処理も若干軽くなるだろう。ただし、絵文字のコード自体は相変わらずキャリアによって異なるので、何らかの対策は必要である。End of Article

カテゴリ:Webフォーム 処理対象:モバイル
関連TIPS:[ASP.NET]Webページに含まれるリンクを動的に変更するには?

この記事と関連性の高い別の.NET TIPS
文字列を数値に変換するには?(TryParse編)
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム Jigsaw(ジグソー) により自動抽出したものです。
generated by

「.NET TIPS」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間