JavaScript
正規表現

JavaScriptでの正規表現のハマりポイント

More than 1 year has passed since last update.

最近ハマったので、メモとして書いておきます。

実験したのはGoogle Chromeの現時点での最新版(34.0.1847.131)ですが、MDNの文書でも同じことが書かれているので、Firefoxでも事情は同様でしょう。

"." は改行文字にマッチしないし、マッチさせるすべがない

var container = document.getElementById("container");
container.innerHTML = container.innerHTML.replace(/===ここから===.*?===ここまで===/, "");

これ、「===ここから===」と「===ここまで===」が一行に収まっていればマッチするのですが、複数行にまたがっている場合にはマッチしませんし、当然置換も行われません。 ドットが改行文字にマッチしないから です。

RegExp - JavaScript | MDN によると、修飾子(flag) には g i m y があるのですが、Perlでドットを改行文字にマッチさせる s 修飾子というものがないので、どうしようもありません。

上述のページにも "." の説明がこう書かれています。

(この文字は小数点です) 改行文字(\n、\r、 \u2028、あるいは、\u2029)を除いたあらゆる 1 文字にマッチします( [\s\S] という正規表現を使えば、改行文字を含めたあらゆる文字にマッチさせることができます)。

というわけで、上記のJavaScriptは以下のように書き換えることで、複数行マッチにも対応することができます。

var container = document.getElementById("container");
container.innerHTML = container.innerHTML.replace(/===ここから===[\s\S]*?===ここまで===/, "");

"^" はどんな場所でもメタ文字のように振る舞う

正規表現の ^

  • 正規表現の冒頭にある場合には、冒頭から開始するという意味
  • 文字クラス ([] で囲われたもの) の先頭にあると、文字クラスの集合の補集合となる

という意味でメタ文字として使われますが、その他の場所ではそれを書いたとしても書いていないようにみなされてしまいます。

(※当初、PerlやPCREでは上記以外のような場所では ^ はその文字自身を表すと書きましたが、間違っていました)

// Chrome デベロッパーコンソールで実験
> str1 = "abc^def"
"abc^def"
> str1.match(/c^d/)
null

この現象を回避して ^ をその文字自身としてマッチさせるには、\ でエスケープをする必要があります。

> str1.match(/c\^d/)
["c^d"]
> re = new RegExp("c\\\\^d") // バックスラッシュ2つ
/c\^d/
> str1.match(re)
["c^d"]

とはいえ、^ を改行の直後にゼロ幅マッチさせるためには、m 修飾子を指定してやる必要があります。

> str2 = "xyz\n123"
"xyz
123"
> str2.match(/^1/)
null
> str2.match(/^1/m)
["1"]