常にマッチする正規表現を使えばJavaScriptでも固定位置でのパターンマッチができる

パターンの末尾に「|」を付け加えると、その正規表現は常にマッチする。

/aaa/.exec("xyz");  // => null
/aaa|/.exec("xyz"); // => [""]

これを使えば、ある位置でのみパターンマッチを行うということ(Perl正規表現で「\G」のメタ文字を使うようなこと)が JavaScript でもできる(Firefox に限っては「y」フラグを使えば簡単にできるけど、独自拡張今のところ他のブラウザでは使えない)。

// RegExp::exec の戻り値の配列には index プロパティがあり、
// マッチに成功した位置を表す

var text = "xxxaaxxxaaa", regexp, result;
// 4〜6文字目が aaa かどうかだけ確かめたいとする

// 通常の正規表現
regexp = /aaa/g;
regexp.lastIndex = 3;   // これで4文字目からのパターンマッチとなる
result = regexp.exec(text);
result[0];    // => "aaa"
result.index; // => 8   // そこまで行かなくていい!

// 常にマッチする正規表現
regexp = /aaa|/g;       // パターンの末尾の | が重要
regexp.lastIndex = 3;
result = regexp.exec(text);
result[0];    // => ""  // 空文字列に一致(実質失敗)
result.index; // => 3   // 4文字目の位置に留まる(無駄な試行をしない)

正規表現ベースのパーサを作るときに使えそう。

id:trashtoyさん

やりたいことは理解できたけど、個人的にsubstrして/^〜/のほうが直感的に分かりやすい(好みの問題?)

はてなブックマーク - trashtoyのブックマーク / 2012年8月1日

先の例で言うと、以下のようなことですね。

text = text.slice(3);
result = /^aaa/.exec(text);
text = text.slice(result[0].length);
// ...

対象の文字列が短いとか、何度もパターンマッチを行うわけではないという場合は、この方法がベストだと思います。

以前、この方法を用いてパーサを書いていたのですが、IE8以下でのパフォーマンスが気になっていました。JavaScript正規表現にも「\G」のようなメタ文字があれば便利なのになあと思いつつそのままにしていたのですが、最近急に思いついたのがこの記事の方法でした。JavaScript におけるパーサの実装方法の比較記事のようなものをいつか書こうと思います。

おまけ

以下は間違い。行頭一致の正規表現でしか使えない。

常に一致する正規表現を使えば、RegExp::exec、String::match の戻り値を配列に統一できる。しかし、分かりにくいのでおすすめしない。

var pattern, result, videoId;

// 通常の正規表現
// exec の戻り値が null になる可能性がある
pattern = /(?:\?|&)v=([_0-9A-Za-z-]+)/;

// 直接プロパティアクセスすると、マッチしなかったときに TypeError が発生
// videoId = pattern.exec(location.search)[1];

// : 一時変数を使う
result = pattern.exec(location.search);
videoId = result ? result[1] : null;

// : OR 演算子を使う
videoId = (pattern.exec(location.search)||[])[1];


// 常にマッチする正規表現(末尾の | が重要)
pattern = /(?:\?|&)v=([_0-9A-Za-z-]+)|/;

// もう null を恐れる必要はない
videoId = pattern.exec(location.search)[1];