5 : 01 PHP4でMozilla, Operaの均等割付

← 4 - 27 p↑ もくじ i 5 - 02 n →

PHP4でMozilla, Operaの均等割付

2002年 2月26日
記事ID d20226a

Mozilla, Opera でも日本語を均等割付」では、PHP3の多バイト対応正規表現を使った。このメモでは、多バイト対応が中途であるPHP4.0.xで同じことを実現する方法を述べるとともに、ゲリラ的だが徹底的な、さらに美しい効果が得られるアルゴリズムも説明する。基本的な論点と関連する技術的文献については「Mozilla, Opera でも日本語を均等割付」を参照。そちらですでに触れたように、本来、均等割付はUA側で実装すべきことであり、PHPのレイヤでどうこうするのは、あくまでアドホックなハックであることに注意。

以下のリストは検証中のベータ・ステータスであり、最適化されていず、間違いを含む可能性がある(コストの都合上、厳密な禁則処理は行わない)。文字コードはEUC-JPとし、アスキー以外の部分は、すべて2バイト文字とする(ふつうはそうだが、厳密な仕様上は、そうでないこともできる)。UTF-8ならもっとシンプルだったろう。

PHP4のコード

PHP4.0.xでは、まだ多バイト対応の正規表現が使えないが、同様のことを低水準に自分でやることができる。

$line = preg_replace( "/([\xA1-\xFE]{2})/e", "_justify($1)", $line );

function _justify( $double_byte_char ) {
    if(
        $double_byte_char == "。" ||
        $double_byte_char == "、" ||
        $double_byte_char == ")" ||
        $double_byte_char == "」"
    ) return "$double_byte_char\t";
    elseif(
        $double_byte_char == "(" ||
        $double_byte_char == "「"
    ) return "\t$double_byte_char";
    elseif(
        $double_byte_char == " "
    ) return "\t$double_byte_char\t"; // 全角スペース
    else
    return( $double_byte_char );
}

EUC-JPの「全角」文字は第一バイト、第二バイトともに 0xA1~0xFE の範囲にあり、バイナリストリームの部分を見ても第一バイトか第二バイトか判別できないから、次のようにマッチさせると意図通りにならない。

# 間違いの例
$line = ereg_replace( "(\xA1\xA2|\xA1\xA3|\xA1\xD7)", "\\1\t", $line );

先行する文字の第2バイトと後続する文字の第1バイトにまたがってマッチしないようにするために、ストリームの最初から見ていく。全部のダブルバイト文字をマッチさせて、1文字ごと(2バイト読むたびに) _justify をコールするのはコストが高いが、第二引数にべたで評価式を書いてもあまりトクにならない。

また、([\xA1-\xFE][\xA1-\xFE]) より ([\xA1-\xFE]{2}) のほうが効率が良いだろう。

次のようにして、全角と半角のあいだでもワードラップを許可することができる。Perl互換で書く場合、置換先のほうは \1 でなく $1 と書くべきだが、古いPHPでは、むしろ $1 では正しく動作しない(最新のPHPならどちらでも動作する)。

$line = preg_replace( "/([^\xA1-\xFE >])([\xA1-\xFE])/", "\\1 \\2", $line);
$line = preg_replace( "/([\xA1-\xFE])([^\xA1-\xFE <])/", "\\1 \\2", $line);

日本語の文章は、原則としてどこで表示行の改行を行っても良いのであるが、すべての文字間に空白類を入れると、空白ひとつぶんは反映されてしまうので、意図した表示にならない。どうしてもやるなら、次のスケルトンの発想を使う。

$line = ereg_replace( "([\xA1-\xFE]{2})", "\\1<span class=\"zero\"> </span>", $line );

ここでクラスzeroは1pxのスペースとする。あらゆる意味で非常にコストが高いが、ここまでやると非常に美しい均等割付が得られる。このスケルトンは#PCDATAに対して実行する。例えばP要素に対してじかに実行すると、インラインのimg要素のalt属性値やa要素のtitle属性値も置換対象になる結果、(単に理論上、間違っているというだけでなく)現実にもタグが壊れて表示が乱れてしまう。

コストの評価

最後の「徹底的」なアルゴリズムは考察外として、それ以外の部分(句読点の前後や全角と半角のあいだでワードラップを許可)だけ考えた場合、おおざっぱに、この処理を行うと、行わない場合の10倍オーダーのCPU時間を消費する。けれど、MozillaやOperaのユーザは比較的少ないので、サーバリソースに余裕があるなら、この処理を実際に試すことができる。現時点では、www.faireal.net のトップページと、/articles/x/yz/ (x≧3) にて、この処理を試験的に有効にしている(2002年2月26日23時~)。最初のメモのようにPHP3国際化版の mb_ereg を使えば処理は単純になるが――ダブルバイト文字の処理部分がコアにプリコンパイルされているので――、PHP3自体の一般的な遅さによって、この処理以外のすべてを考えあわせたトータルではPHP4のほうが有利だ。

この記事のURL

テキスト版省パケ版XML版



webmaster@faireal.net