SOFTELメモ

</> 技術者募集

【Javascript】正規表現でgフラグを付けても phpのpreg_match_all()のようなことができない

問題

これで、abc,def,ghi が取れないんだけど。

"123abc456def789ghi".match(/\d+([a-z]+)/g);

phpのpreg_match_all()などだと、マッチした文字列も、キャプチャした文字列も、どこかでまとめて取れるけど、Javascriptだと無理?

答え

まず、Javascriptの正規表現のmatch()はgフラグをつけているかどうかで戻り値が違う。

“123abc456def789ghi”.match(/\d+([a-z]+)/g);
→ “123abc”,”456def”,”789ghi”
 (1回目適用、2回目適用、3回目適用、……)

“123abc456def789ghi”.match(/\d+([a-z]+)/);
→ “123abc”,”abc”
 (1回目適用、キャプチャされたもの1、キャプチャされたもの2、キャプチャされたもの3、……)

String.match だとこれ以上どうしようもないので、

正規表現で “g” フラグを使用する場合、同じ文字列で成功するマッチを見つけるために exec メソッドを複数回使うことができます。

という RegExp.execを使ってみると、「次のマッチが始まる位置(lastIndex)」を確認しながら、ループする形になる。

var myRe=/\d+([a-z]+)/g;
var str = "123abc456def789ghi";
var myArray;
while ((myArray = myRe.exec(str)) != null) {
    var msg = myArray.shift() + " を見つけました。";
    msg += "キャプチャされたのは " + myArray + " です。";
    msg += "次のマッチは " + myRe.lastIndex + " からです。";
    alert(msg);
}

Javascriptでの上の例を見ると、phpのpreg_match_all()関数は、本来は上のようになるところを、めんどくさくないようにしてくれている便利機能なんだなと考えることができそうです。

補足

もっと簡単にabc,def,ghiを取るには、’123abc456def789ghi’全体にマッチする正規表現を作って、その中でabc,def,ghiをキャプチャできるようにする。

//キャプチャしたかったらこうなるか……もっとよい書き方がある?

"123abc456def789ghi".match(/\d+([a-z]+)\d+([a-z]+)\d+([a-z]+)/);

//→ キャプチャされるのは"abc","def","ghi"
//キャプチャ用パターンを繰り返したとき
//キャプチャされるのは繰り返しの最後でマッチした部分文字列なので

"123abc456def789ghi".match(/(\d+([a-z]+))+/g);

//→ 文字列全体がマッチするのは期待通りだが、キャプチャされるのは"789ghi","ghi"だけ

//置換などで何が何番目のキャプチャかわからないと「\何番目」と書くとき困るから仕方ないか

参考

https://developer.mozilla.org/ja/Core_JavaScript_1.5_Reference/Global_Objects/RegExp/exec

関連するメモ

コメント(1)

sutara_lumpur 2012年2月6日 09:38

私の場合は下記のように『g』オプションを使わずに、
マッチしたものの右側の文字列のみに切り詰めていく方法を
考えていたのですが…、こちらのほうが断然シンプルでいいですね(*´∀`*)

var myRe=/\d+([a-z]+)/; //『g』オプションは使わず
var str = “123abc456def789ghi”;
var myArray;

var str2 = str; //使い捨て用にコピーを… (^ ^;)
while ((myArray = str2.match(myRe)) != null) {
alert(myArray[1]);
str2 = RegExp.rightContext;
}