モバイルサイトを構築するための文字コード知識
はじめに
携帯キャリア向けのサイトを作っている時に必ず発生する文字コード問題(主に文字化けやByte数問題)。この記事では文字コード(文字集合、符号化)に対する内容についても触れつつ、モバイルサイト作りで注意すべき点について詳しく記述する。
そもそも文字コードとは
以下では簡単に説明する。
- 一つずつの文字を表すためのByte列表現であり、Byte列表現と文字の対応関係でもある。
- 別の言い方をすると文字コードとは各種文字についての符号の番号を独自の順序と計算式で示したByte列表現。
- 言語の文化と密接な関係を持っており、例えば英語圏、アジア圏でそれぞれ固有の文字コードが制定されるが、コンピュータ上で正常に表現できない言語も多い。
- 日本語を表現する有名な文字コードとしてはISO-2022-JP(JIS)、EUC-JP、Shift_JIS、UTF-8などである。
- 1バイト系文字コード(シングルバイト、半角文字)と2バイト系文字コード(マルチバイト、全角文字)
- 色々な文字コードが存在するため、システムで変換するときには様々な問題が起こる。変換には処理コストも掛かるし、対応が正確でないこともある。
- Unicodeは上の互換性の問題に対応するために作られ、全ての言語を表現する。
- 文字コードは文字集合(coded character set)と符号化方式(character encoding scheme)という区別がされる。
各キャリアサイトの文字コード
各キャリアの技術サイトを見てみる。
マトリックスでまとめると以下のようになる。
キャリア | Shift_JIS | ISO-2022-JP | EUC-JP | UTF-8 | 備考 |
---|---|---|---|---|---|
DoCoMo | ○ | × | × | ○ | htmlだとUTF-8が使用不可 |
Au | ○ | × | × | × | formのデータ送信がページ文字コードによらずShift_JISになる |
SoftBank | ○ | ○ | ○ | △ | UTF-8のサポートは機種による |
Willcom | ○ | ○ | ○ | △ | JRCの端末は一部UTF-8未対応 |
emobile | ○ | ○ | ○ | ○ | |
スマートフォン | ○ | ○ | ○ | ○ | ほとんどPCと同じだが、タグの指定とOSのバージョンで一部文字化けすることも |
- この結果からキャリアのサイトを作る場合はShift_JISで作る事が望ましい。UTF-8は完全に対応できてはいない。DoCoMo,Auの制約が厳しい。
- スマートフォンの文字コード制約は特にないので適切なサーバサイドから渡す文字コードとクライアントのContent-Typeの設定を一致させる(UTF-8が一般的か)
- xhtmlのContent-Type指定のサンプルは次のように設定。
<?xml version="1.0" encoding="Shift_JIS"?> <!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja"> <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS" />
クライアント/サーバ間で起きる問題
上の内容からスマートフォンサイト以外は基本的に表示するサイトのShift_JISで統一して構築することが望ましいが(charsetをShift_JISに設定)、サーバサイドの文字コードはUnicode(UTF-8)で構築されることが多く、クライアント/サーバ間の違いにより色々な問題が起きる。以下には注意すべき事を記述する。
その1. サーバサイドの各種文字コードの設定
1.DBの文字コード設定
$ vi /etc/my.cnf [client] default-character-set=utf8 [mysqld] default-character-set = utf8 skip-character-set-client-handshake character-set-server = utf8 collation-server = utf8_general_ci init-connect = SET NAMES utf8
- データベース全体の文字コード設定を調べる。
mysql> show variables like 'character_set%';
- データベースごとの文字コード設定を調べる。
mysql> show create database データベース名;
- テーブルごとの文字コード設定を調べる。
mysql> show create table データベース名;
2. 各種プログラムファイルの文字コード設定
$ nkf --guess <file> <-- 文字コード調査 EUC-JP (LF) $ nkf -w --overwrite <file> <-- UTF-8変換してfileを上書き
- .htaccessに以下のような設定をして、phpを扱う時の入出力文字コードを切り替える方法。以下の例はサーバの内部処理はUTF-8、クライアントへの出力をShift_JISとしている。この記述によりPHPが自動的に変換してくれる。
php_flag output_buffering On php_value output_handler mb_output_handler php_value default_charset Shift_JIS php_value mbstring.language japanese php_flag mbstring.encoding_translation On php_value mbstring.http_input auto php_value mbstring.http_output SJIS php_value mbstring.internal_encoding UTF-8 php_value mbstring.substitute_character none
説明表
表記 | 説明 |
---|---|
output_buffering | 出力バッファリングの有無 |
output_handler | スクリプトの全ての出力を関数にリダイレクトすることがでる ここにmb_output_handlerを設定すると自動変換してくれる |
default_charset | HTTPのContent-typeヘッダで出力する文字コード |
mbstring.language | mbstringで使用される言語のデフォルト値 |
mbstring.encoding_translation | HTTP入力文字エンコーディング検出および内部文字エンコーディングへの変換を行うか |
mbstring.http_input | HTTP入力文字エンコーディングのデフォルト値 |
mbstring.http_output | HTTP出力文字エンコーディングのデフォルト値 |
mbstring.internal_encoding | 内部文字エンコーディングのデフォルト値 |
mbstring.substitute_character | 無効な文字を代替する文字 |
- Apacheのバージョン次第ではAddDefaultCharsetというオプションにより文字化けが発生してしまうので、httpd.confの記述に注意する。これは強制的に文字コードを指定してしまうのでOffにしてしまうのが良さそう。AddDefaultCharsetディレクティブ
AddDefaultCharset Off
4.その他
サーバサイドの各文字コードの設定がバラバラでも言語上である程度吸収が可能。
以下の例はPHPでの場合
<?php //内部encodingの決定 mb_internal_encoding( 'UTF-8' ); //データベースの接続文字コードを指定 $sql = 'SET NAMES utf8'; if( !mysql_query( $query ) ) { echo 'invalid query'; } //パラメータの文字コードを変換 UTF-8→Shift_JIS $name = $_GET[ 'name' ]; $convert_name = mb_convert_encoding( $name, 'Shift_JIS', 'UTF-8' );
その2. 文字コード変換の問題
文字コードの差異がいくつかあるので注意する必要がある。
- Byte数の違い
- 変換結果が変わる問題
1.Byte数の違い。
- モバイルサイトを作る時にデータの入力や表示にByte数で制限を設けることがある。
- 制限をかける基準をクライアント/サーバのどちらに置くかにもよるが、クライアントを基準にByte数を計算する場合はサーバサイドのByte数処理で気をつけなければならない。(Shift_JISのマルチバイト文字は2Byteでの計算になるが、Unicode系(UTF-8など)は0x0800〜0xFFFFの範囲で3Byteとなる)
- サーバサイドの文字設定がUTF-8の場合、クライアントのByte数に基準を置く時は一度Shift_JISに変換して文字のByte数を計る。
以下はPHPでの例を考える
<?php $data = $_GET[ 'data' ]; //文字コードを変更 Shift_JISを基準にして計るため一度変換 $data = mb_convert_encoding( $data, 'Shift_JIS', 'UTF-8' ); //Byte数を計る $len = strlen( $data ); echo $len; //8Byte以下の制限を設ける場合 if( $len > 8 ) { echo "byte error"; } //元に戻す $data = mb_convert_encoding( $data, 'UTF-8', 'Shift_JIS' );
2.変換結果が変わる問題
- 上のUTF-8→Shift_JIS、Shift_JIS→UTF-8での変換では問題が起きるケースがある。それはShift_JISではサポートされない文字列がうまく変換されないという事である。(例えば①のような文字)
- 2回文字コードを変換しているが2回目の戻した時に元のデータとして表示されない。
- 似たような内容としてWAVE DASH問題、FULLWIDTH TILDE問題といったものもある。
問題となる点をPHPのサンプルコードで見てみる。
<?php // ①の定義 $data = '①'; //dataとascii値の出力 echo "data = $data, ascii = " . ord( $data ) . "\n"; //UTF-8→Shift_JISに変換 $data = mb_convert_encoding( $data, 'Shift_JIS', 'UTF-8' ); //Shift_JIS→UTF-8に変換 $data = mb_convert_encoding( $data, 'UTF-8', 'Shift_JIS' ); echo "data = $data, ascii = " . ord( $data );
実行結果
data = ①, ascii = 226 data = ?, ascii = 63
問題を解決したコードは次の通り
<?php //①の定義 $data = '①' //dataとascii値の出力 echo "data = $data, ascii = " . ord( $data ) . "\n"; //UTF-8→Shift_ISに変換 $data = mb_convert_encoding( $data, 'cp932', 'UTF-8' ); //Shift_JIS→UTF-8に変換 $data = mb_convert_encoding( $data, 'UTF-8', 'cp932' ); echo "data = $data, ascii = " . ord( $data );
実行結果
data = ①, ascii = 226 data = ①, ascii = 226