Y's note

Web技術・プロダクトマネジメント・そして経営について

本ブログの更新を停止しており、今後は下記Noteに記載していきます。
https://note.com/yutakikuchi/

モバイルサイトを構築するための文字コード知識

はじめに

携帯キャリア向けのサイトを作っている時に必ず発生する文字コード問題(主に文字化けやByte数問題)。この記事では文字コード(文字集合、符号化)に対する内容についても触れつつ、モバイルサイト作りで注意すべき点について詳しく記述する。

そもそも文字コードとは

以下では簡単に説明する。

  • 一つずつの文字を表すためのByte列表現であり、Byte列表現と文字の対応関係でもある。
  • 別の言い方をすると文字コードとは各種文字についての符号の番号を独自の順序と計算式で示したByte列表現。
  • 言語の文化と密接な関係を持っており、例えば英語圏、アジア圏でそれぞれ固有の文字コードが制定されるが、コンピュータ上で正常に表現できない言語も多い。
  • 日本語を表現する有名な文字コードとしてはISO-2022-JP(JIS)、EUC-JP、Shift_JISUTF-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の文字コード設定/調査
  2. 各種プログラムファイルの文字コード設定
  3. Apache文字コード設定
  4. その他

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コマンドを使用してファイルの文字コードを確認する。UTF-8に変換したい場合のコマンドも記述する。
$ nkf --guess <file>     <-- 文字コード調査
EUC-JP (LF)
$ nkf -w --overwrite <file>   <-- UTF-8変換してfileを上書き


3.Apache文字コード設定

  • .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 無効な文字を代替する文字
AddDefaultCharset Off


4.その他
サーバサイドの各文字コードの設定がバラバラでも言語上である程度吸収が可能。

  • 言語上で内部encodingを指定する事が可能。
  • データベースの接続文字コードを設定する事も可能。
  • 言語上で文字コードの変更が可能。

以下の例は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. 文字コード変換の問題

文字コードの差異がいくつかあるので注意する必要がある。

  1. Byte数の違い
  2. 変換結果が変わる問題

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-8Shift_JISShift_JISUTF-8での変換では問題が起きるケースがある。それはShift_JISではサポートされない文字列がうまく変換されないという事である。(例えば①のような文字)
  • 2回文字コードを変換しているが2回目の戻した時に元のデータとして表示されない。
  • 似たような内容としてWAVE DASH問題、FULLWIDTH TILDE問題といったものもある。

問題となる点をPHPのサンプルコードで見てみる。

<?php

// &#9312;の定義 
$data = '&#9312;';
//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 = &#9312;, ascii = 226
data = ?, ascii = 63
  • 上の問題を防ぐ方法としてShift_JISの拡張文字コードであるcp932を利用する。cp932はShift_JISに比べてサポートしている文字種が多い。

問題を解決したコードは次の通り

<?php

//&#9312;の定義 
$data = '&#9312;'
//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 = &#9312;, ascii = 226
data = &#9312;, ascii = 226

まとめ

本サイトのまとめ内容を以下に記す。

  • キャリアのサイト構築をする場合クライアント側の文字コードShift_JISで行うと良い。
  • サーバサイドはUnicode系(UTF-8など)で構築するのが一般的になっている。
  • 言語上での設定も可能だが、DB、Apache、ファイルの文字コードは適切に行う。
  • 文字コードによってByte数のカウントが違うためByte数に寄る制限がある場合、クライアント/サーバのどちらに基準を合わせるかによって処理が異なる。
  • UTF-8 <=> Shift_JIS文字コード変換は適切に行われない事があるので、代替方法としてUTF-8<=>cp932を利用する。