意外と知られていないJavaScriptの正規表現(RegExp)の使用方法

JavaScript正規表現を使用するメソッド

JavaScript正規表現を使用するメソッドはtest, match, search, exec, replaceなどがある。

正規表現にマッチするかどうかを調べるだけならmatchなどではなくtrueかfalseを必ず返すtestメソッドを使用すると良い。

JavaScriptで文字列の有無を調べるにはindexOfではなくtestを使う。

量指定子でm回以下は不可

{n,}でn回以上という量指定子でよく使用される正規表現が使用できるが{,m}でm回以下という指定はできないため注意が必要。

var reg = /ab{,3}c/;
console.log(reg.test('abbbbc'));
// => false

console.log(reg.test('abbc'));
// => false

Rubyなどの正規表現でm回以下が指定可能な言語の経験者が間違えやすい。

regexp.toSource()は無効

regexp.toSource()で正規表現のパターンregexpを文字列として返すと解説しているサイトが結構あるが、実際は文字列を返さない。

new RegExpの使い所

new RegExp('foo|bar')と書いても/foo|bar/と書いても同じなので、短い/foo|bar/がよく利用されるがnew RegExpを使用したほうが良いケースが3パターン存在する。

1. マッチを配列で受け取る

new RegExpなら配列をjoin('|')で1つの文字列にしたものを使用できる。

2. 条件のマッチ数が多い

/Photoshop|Illustrator|Dreamweaver/ と書くよりもnew RegExpとjoinを使用したほうが改行を追加できるため見やすくなる。

var reg = new RegExp([
  'Photoshop',
  'Illustrator',
  'Dreamweaver'
].join('|'));

var reg = /Photoshop|Illustrator|Dreamweaver/

3. スラッシュのエスケープ不要

URLなどの場合は /http:\/\/iwb.jp\// のようにスラッシュをバックスラッシュでエスケープする必要があるが、new RegExpだとスラッシュのエスケープは不要になる。

ただしエスケープのバックスラッシュは2つ必要。

// どちらも同じ結果になる
var reg = new RegExp('http://iwb\\.jp/');
var reg = /http:\/\/iwb\.jp\//;

console.log(reg.test('I have a pen.'));
// => false

console.log(reg.test('My site is http://iwb.jp/'));
// => true

肯定的先読みは可能、肯定的後読みは不可

先読みはほとんどのブラウザで使用可能だが後読みはChrome以外のブラウザはほとんど使用不可。

// 肯定的先読み => true
console.log(/B(?=C)/.test('ABCD'));

// 否定的先読み => true
console.log(/B(?!D)/.test('ABCD'));

// 肯定的後読み => true
console.log(/(?<=A)B/.test('ABCD'));

// 否定的後読み => true
console.log(/(?<!C)B/.test('ABCD'));

肯定的先読みと否定的先読みを組み合わせて使用すれば数字のカンマ区切りなどもできるようになる。

var s = '10000円は1000円より大きい。';
var r1 = String(s).replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1,');
console.log(r1);
// => 1,000,000円は1,000円より大きい。

後方参照は\1ではなく$1を使用する

JavaScript後方参照は\1ではなく$1を使用するため以下コードは無効。

var s = '1234-5678-9999-0000';
var r = String(s).replace(/(\d{4})\-/g, '\1');
console.log(r);

さらに下記のような使い方も無効だ。

var s = '1234-567-9999-000';
var r = /(\d{4})\-(\d{3})\-(\1)\-(\2)/.test(s);
console.log(r); // => false

// $1に変えても使えない
var r = /(\d{4})\-(\d{3})\-($1)\-($2)/.test(s);
console.log(r); // => false

使用可能な正規表現フラグはg, i, m, u, y

ほかの言語で使用されているo, s, e, xは使用できないため注意。

フラグ 説明
g グローバルサーチ
i 大文字・小文字を区別しない検索
m 複数行検索
u unicodeパターンの扱い変更
y lastIndexで指定したマッチの位置から検索を開始

g, i, m は説明不要だがu, yは知らない人が多い。

uは「.」などのパターンを絵文字などでも1文字として扱う。

var str = '🍔🥗🥤';

console.log(/^.🥗./.test(str));
// => false

console.log(/^.🥗./u.test(str));
// => true

yはlastIndexで指定したマッチの位置から検索を開始できる。例えば4文字目からマッチするか調べるには下記のようになる。

var str = 'foo-bar-baz';
var regex = /bar/y;

// 4文字目からbarがあるためtrue
regex.lastIndex = 4;
console.log(regex.test(str));

// lastIndexは自動的に0にリセットされるためfalse
console.log(regex.test(str));
console.log(regex.lastIndex); // 0

// 5文字目からbarがあるためfalse
regex.lastIndex = 5;
console.log(regex.test(str));