PHPでマルチバイト(日本語)に対して正規表現を使って後方参照したかった

PHPでマルチバイト(日本語)に対して正規表現を使って後方参照したかった。ちょっと正規表現でごにょごにょしてるとつっかえたことがあったのでメモ。

まず、次のような試験がうまく通らなかった。



<?php

$str[] = '1あああ2'; // 1) hit => OK
$str[] = '1ああ)あ2'; // 2) not hit => OK
$str[] = '1ららら2'; // 3) not hit => NG
$str[] = '1らら)ら2'; // 4) not hit => NG
$ptrn = '/1([^\)]*)2/';

foreach($str as $val){
if(preg_match($ptrn, $val, $matches)){
var_dump($matches);
}
}


この例の場合、”あ”が検査対象となっている場合には思った通りの動作をしてくれる。でも”ら”が入った文字列には思い通りの動作にならない。

いろいろ悩んだところ、これはマルチバイトの問題だろう,というところに行き着いた。

まず最初に、preg_matchのマルチバイト版がないか探した。mb_preg_match的な関数で探した。でも無い。残念。

どうやらmb_ereg系はあるらしい。でも最近のPHP (5.3以降)ではeregじゃなくてpreg_matchが推奨されている。

>PHP: PHP 5.3.x で推奨されない機能 – Manual

そしてmb_eregを使うと後方参照(あとで括弧を使って正規表現で合致した文字列の一部を利用できるようになる機能)が使えない。なのでmb_eregは今回の解決方法対象外。

で、ぐぐってると正解にたどり着けた。

どうやら検査パターンの最後のオプションにuを付けるとイイらしい。おそらくutf-8で解釈しなさいよ、ってことかな。

preg系でオプションを付けるには終了デリミタ(って言えば良いのかな)の後ろに文字を列挙して指定する。今回の場合にはこんな感じ。


<?php

$str[] = '1あああ2'; // hit => OK
$str[] = '1ああ)あ2'; // not hit => OK
$str[] = '1ららら2'; // not hit => NG
$str[] = '1らら)ら2'; // not hit => NG
$ptrn = '/1([^\)]*)2/u';

foreach($str as $val){
if(preg_match($ptrn, $val, $matches)){
var_dump($matches);
}
}

?>

原因についても考えてみた。今回は文字(列)”ら”と文字(列)”)”が問題を起こしていた。双方のUTFコードは以下の通りである。

ら E38289

)EFBC89

パっと見た感じ、下二バイトが合致してるとダメみたい。試しに同様に下2バイトが合致する”ド”で試したら案の定”)”と区別できなくなってた。ほむー。

JavaScript Unicode Charts

コメントを残す