あるかどうかだけなら test() です。真偽値が返ります。
取り出したいなら match() です。
見つからないときは空配列ではなく nullです。そのまま [0] を読むと落ちます。
使い分け
| したいこと | 使うもの | 見つからないとき |
|---|---|---|
| あるか無いか | re.test(s) |
false |
| 最初の1つを取り出す | s.match(re) |
null |
| 全部取り出す | [...s.matchAll(re)] |
空配列 |
| 位置だけ欲しい | s.search(re) |
-1 |
| 置き換える | s.replace(re, ...) |
元のまま |
null が返るのは match() だけです。ここが事故の起点になります。
try {
[...'a1'.matchAll(/\d/)];
} catch (error) {
console.log(error.name);
}g を付けると戻り値の形が変わる
const re = /a/g;
console.log(re.test('aa'), re.lastIndex);
console.log(re.test('aa'), re.lastIndex);
console.log(re.test('aa'), re.lastIndex);見た目は似ていますが中身が違います。
gなし … 最初の1つ。[0]が一致した文字列、[1]以降がキャプチャgあり … 一致した文字列だけを並べた配列。キャプチャは消える
g を付けると「どこで一致したか」も「括弧の中身」も失われます。
全部を、中身つきで取り出す
matchAll() を使います。
一致ごとに、キャプチャも位置も持ったまま取れます。
戻り値はイテレータなので、[...] で配列にしてから使います。
g を忘れると例外になります。
文言は「String.prototype.matchAll called with a non-global RegExp argument」(Node 22.22.3)。処理系によって変わります。
名前を付けて取り出す
(?<名前>…) と書くと、groups から引けます。
番号で数えるより壊れにくくなります。
括弧を1つ足しただけで m[2] が別のものを指す、という事故が起きません。
いちばんの落とし穴:g 付きの test()
同じ文字列を3回調べたのに、3回目だけ false です。
g 付きの正規表現は前回どこまで見たかを lastIndex に覚えています。
test() はそこから探し始めるので、呼ぶたびに結果が変わります。
g を外せば起きません。
「あるか無いか」を調べる正規表現に g は要りません。
使い回す正規表現を定数に置いているときほど当たりやすい罠です。
特殊文字を打ち消す
. は「任意の1文字」です。そのまま書くと別の意味になります。
文字列から正規表現を作るときは、さらに気をつけてください。
new RegExp() に渡すのは文字列なので、\ を打ち消すのに \\ が要ります。
利用者が入力した文字列を、そのまま正規表現にしないでください。 意図しない一致に加えて、書き方によっては処理が極端に遅くなることがあります。 探すだけなら
includes()で足ります。
打ち消しを行う RegExp.escape() は新しい提案で、Node 22.22.3 にはまだありません。
よく使う形
^と$で全体が一致するかを見る(付けないと一部でも通る)\s+で連続した空白をまとめる- 置き換えに関数を渡せる
$1でキャプチャを並べ替えられる
まとめ
- あるか無いかは
test()、取り出すならmatch()、全部ならmatchAll() match()は見つからないとnull。?.で守るgを付けるとmatch()の戻り値からキャプチャが消えるmatchAll()はgが必須。位置もキャプチャも残るg付きのtest()は呼ぶたびに結果が変わる(lastIndex)- 名前付きグループを使うと、括弧を足しても壊れない
- 文字列から作るときは
\\で打ち消す。利用者の入力をそのまま渡さない
正規表現を使わずに済むなら、そのほうが読みやすくなります。 探すだけ・分けるだけの場面は、こちらにまとめてあります。 → 文字列を探す・分ける・置き換える