JavaScript正規表現でマッチ(match)の基本と実践
文字列から「パターンに合う部分」を見つけて取り出すなら match。1件だけか、すべてか、グループ抽出かで挙動が変わるので、最初に仕組みを押さえると迷いません。
構文と返り値のルール
const result = s.match(regex);
JavaScript- 返り値(gフラグなし): 最初の一致+キャプチャを含む「配列風オブジェクト」。見つからなければnull。
- 返り値(gフラグあり): すべての一致を「純粋な配列」で返す(キャプチャグループは含まれない)。見つからなければnull。
- 覚え方: 「グローバルあり → 全件の生テキスト」「グローバルなし → 先頭1件+グループ詳細」。
まずは基本テンプレート
最初の一致を1件だけ取得(グループ付き)
const s = "Item-123 and Item-456";
const m = s.match(/Item-(\d+)/); // gなし
console.log(m[0]); // "Item-123"(一致全体)
console.log(m[1]); // "123"(キャプチャグループ1)
JavaScript- ポイント: 最初の一致だけ詳細に取れる。後続の一致は対象外。
すべての一致を取得(単純抽出)
const s = "Aki, Mao, Ren";
const names = s.match(/[A-Z][a-z]+/g); // gあり
console.log(names); // ["Aki","Mao","Ren"]
JavaScript- ポイント: 全件を配列で返すが、各要素は「一致テキストのみ」。グループ詳細は不要な場面向き。
よく使うパターン別レシピ
メールアドレスらしき文字列を抽出(簡易)
const s = "Contact: aki@example.com, cc: info@test.jp";
const emails = s.match(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g);
console.log(emails); // ["aki@example.com","info@test.jp"]
JavaScript- ポイント: 実運用は専用バリデータ推奨。ここでは軽量抽出。
数字とラベルのセットをグループで抜く
const s = "Price: 120, Qty: 3";
const m = s.match(/Price:\s*(\d+),\s*Qty:\s*(\d+)/);
console.log(m[1], m[2]); // "120" "3"
JavaScript- ポイント: gなしで「1件+グループ」。後段で数値化するなら Number() を。
改行をまたいで囲いの中身を抜く(最短一致)
const s = "BEGIN\nline1\nline2\nEND";
const m = s.match(/BEGIN([\s\S]*?)END/);
console.log(m[1]); // "\nline1\nline2\n"
JavaScript- ポイント: ドットが改行にマッチしないので「[\s\S]」で全てにマッチ+「*?」で最短一致。
複数行で「行頭」を扱う(mフラグ)
const s = "apple\nbanana\nApricot";
const linesStartingWithA = s.match(/^A\w+/gm);
console.log(linesStartingWithA); // ["Apricot"]
JavaScript- ポイント: mフラグで ^ と $ が「各行頭・行末」になる。
捕捉:matchAllで「全件+グループ」も取りたい
const s = "ID=12; ID=34;";
const re = /ID=(\d+)/g;
const all = [...s.matchAll(re)];
console.log(all[0][0], all[0][1]); // "ID=12" "12"
console.log(all[1][0], all[1][1]); // "ID=34" "34"
JavaScript- ポイント: matchAllはg必須。全件分のキャプチャグループが欲しいときの最適解。
testとの使い分け(存在判定だけなら)
const re = /\d+/;
console.log(re.test("No 123")); // true
JavaScript- ポイント: true/falseだけ欲しいなら test。テキスト抽出なら match/matchAll。
フラグの要点(迷ったらこれ)
- g(global): 全件取得。matchは配列、matchAllはグループも含む。
- i(ignoreCase): 大文字小文字を区別しない。
- m(multiline): 行頭/行末を各行で扱う。
- s(dotAll): ドットが改行にもマッチ(ES2018+)。
/./sで改行含め「何でも」扱える。 - u(unicode): Unicode対応で文字クラスや量指定を安定化。
よくある落とし穴と対策
- gありだとキャプチャが取れない: matchの返りは「一致文字列のみ」。グループが必要なら matchAll を使う。
- 改行にドットがマッチしない:
.は改行を除く。[\s\S]またはsフラグを使う。 - 欲しい部分が長すぎる/短すぎる:
*は貪欲、*?は最短一致。必要に応じて「?」を使って調整。 - エスケープ不足:
.*+?()[]{}|^$\はメタ。リテラルとして使うなら\で逃がす。 - nullで落ちる: 一致なしはnull。
const m = s.match(re) || [];のようにガードしてから参照。
練習問題(手を動かして覚える)
- 1件+グループ抽出(価格と数量)
const s = "Price: 800, Qty: 2";
const m = s.match(/Price:\s*(\d+),\s*Qty:\s*(\d+)/);
console.log(m ? [m[1], m[2]] : null); // ["800","2"]
JavaScript- 全件抽出(単語の先頭大文字)
const s = "Hello world from JS and Node";
console.log(s.match(/\b[A-Z]\w+/g)); // ["Hello","JS","Node"]
JavaScript- 改行を含む囲いの中身(最短一致)
const s = "<code>\nline1\nline2\n</code>";
const m = s.match(/<code>([\s\S]*?)<\/code>/);
console.log(m && m[1].trim()); // "line1\nline2"
JavaScript- matchAllで全件+ID抽出
const s = "user(id=1), user(id=2)";
const entries = [...s.matchAll(/id=(\d+)/g)].map(m => Number(m[1]));
console.log(entries); // [1,2]
JavaScript直感的な指針
- 最初の1件+グループ詳細: match(gなし)。
- 全件をざっと抽出: match(gあり)。
- 全件+グループ詳細まで欲しい: matchAll(gあり)。
- 存在確認だけ: test。
