正規表現を使いまわすと正しく検索できないケース – JavaScript

JavaScriptで正規表現を作成するには

  • 正規表現リテラルを使用する
  • RegExpオブジェクトを使用する

の2通りの方法があります。

//正規表現リテラル
const reg = /\d{4}/g:

//RegExpオブジェクト
const reg = new RegExp(/\d{4}/,"g");

この正規表現を用いると、test()やexec()メソッドを使って文章中の文字列を検索したりすることができます。

しかし、正規表現を使いまわそうとすると意図した動作にならないことがあります。

正規表現の落とし穴!?

例えば以下の複数の文字列に対して、正規表現で4桁の数値を取得してみます。

const reg = /\d{4}/g;
const data1 = "a1234";
const data2 = "b3842";

let match;

match = reg.exec(data1);
console.log(match); // ["1234", index: 1, input: "a1234", groups: undefined]
match = reg.exec(data2);
console.log(match); // null

2つ目の文字列を検索しようとすると”3842″ではなく”null”が返ってきてしまいます。

“g”フラグの挙動

この原因は”g”フラグ(グローバルサーチ)の仕様によるものです。

gフラグをつけて検索を行うと、次のマッチが始まる位置(lastIndex)を保持します。

上記の例の場合、まずdata1=”a1234″に対して検索を行います。この時”1234″にマッチしますので、次のマッチが始まる位置lastIndexは”5″となります。

次にdata2=”b3842″に対して検索すると、文字列の先頭からではなくlastIndexの位置から検索が行われます。そのためマッチする文字列がなくnullを返します。

例えばdata2を”b38429382″とすると”9382″がマッチします。

const reg = /\d{4}/g;
const data1 = "a1234";
const data2 = "b38429382";

let match;

match = reg.exec(data1);
console.log(match); // ["1234", index: 1, input: "a1234", groups: undefined]

console.log(reg.lastIndex) // 5

match = reg.exec(data2);
console.log(match); // ["9382", index: 5, input: "b38429382", groups: undefined]

gフラグと正しく付き合う

このようにgフラグ付きの正規表現を使いまわそうとすると、意図しない動作に陥る場合があります。

では「gフラグは悪か?」というとそうではありません。

lastIndexを保持するということは、以下のような反復処理が可能となります。

const reg = /\d{4}/g;
let match;
while(match = reg.exec("1234-5678-9012")){
  console.log(match[0]); //1234, 5678, 9012
}

whileと組み合わせることでマッチする文字列(この場合は4桁の数字)を順に取得することができます。

 

一見すると正規表現の処理は使いづらそうに見えますが、「gフラグを付けるとlastIndexを保持する」ということを知っておけば、正規表現の処理は強力な武器となりえるでしょう。

Related Posts