javascript match()完全ガイド:戻り値・正規表現・落とし穴まで解説

この記事ではJavaScriptのString.prototype.match()の構文・引数・戻り値を整理し、正規表現での文字列検索/抽出、g・iフラグやキャプチャ/名前付きグループ、引数なし・非RegExp指定時の挙動まで具体例で解説。全件取得や戻り値の違いなどの迷いを解消できます。

目次

JavaScriptのmatch()とは:できることと基本イメージ

javascript+match+regexp

JavaScriptのmatch()は、文字列に対して「特定のパターン(多くの場合は正規表現)」が当てはまるかを調べ、当てはまった部分を取り出すためのメソッドです。キーワードの通り、javascript matchを理解するうえで重要なのは、単なる“検索”ではなく、検索結果をそのまま抽出データとして扱える点にあります。

基本イメージは次の通りです。

  • 文章(文字列)の中から、条件に合う部分を見つける
  • 見つかった文字(または文字のまとまり)を結果として受け取る
  • メールアドレス、URL、日付、IDのような“形式がある文字列”の扱いに強い

たとえばログ、フォーム入力、CSVの1行など、「規則があるテキスト」から必要な情報だけを抜き出したいときに、match()は第一候補になりやすいメソッドです。

match()が得意な用途(検索・抽出・検証の入口)

match()が特に力を発揮するのは、次の3つの入口業務です。いずれも、テキスト処理の現場で頻出するパターンです。

  • 検索:特定の文字列パターンが含まれているかを探す(例:エラーメッセージ内のコードらしき部分)
  • 抽出:一致した部分を取り出して、後段の処理に渡す(例:文章から日付や番号を抜く)
  • 検証の入口:入力が“形式として”妥当そうかを確認する第一段階にする(例:IDが英数字のみか、桁数が合うか)

ポイントは、match()が「テキストに含まれる情報を見つける」だけでなく、見つけた結果をデータとして扱うための起点になりやすいことです。たとえば「注文番号っぽいものがあれば抽出して、その後にDB照会する」「日付っぽいものを拾って、パースして整形する」といった流れの最初の一手として使われます。

また、次のような“境界が曖昧な文字列”でもパターンで切り取れるため、単純なindexOf()のような一致検索では難しいケースに強いです。

  • 桁数が変動する番号(例:#123#123456
  • 区切り文字が混在する日付(例:2026-01-252026/01/25
  • 大文字小文字が揺れる識別子(例:ABCabc

つまり、“文字列から意味のある塊を取り出す”という観点で、javascript matchは実務で非常に出番が多いメソッドです。

正規表現(RegExp)の基礎を先に押さえる

match()の理解をスムーズにするには、正規表現(RegExp)が「文字列の検索条件を表すための専用ルール」だと捉えるのが近道です。正規表現は“難しそう”に見えますが、最初は「よく使う部品」を押さえるだけで十分に戦えます。

まず知っておきたいのは、正規表現が次のような要素を組み合わせてパターンを表現することです。

  • 文字:その文字に一致(例:abc
  • 文字クラス:候補の集合(例:数字っぽい、英字っぽい)
  • 量指定:何回繰り返すか(例:一定の桁数)
  • 境界:単語の区切りや行頭・行末など(例:単語として成立しているか)

さらに、match()でよく一緒に登場するのが、正規表現のフラグです。フラグは「検索のしかた」を変えるスイッチだと考えると理解しやすくなります。

  • 大文字小文字を区別するか
  • 最初の一致だけで止めるか、全部拾うか
  • 複数行のテキストをどう扱うか

正規表現を先に押さえることで、match()は「文字列メソッドの一つ」から、「パターンに基づいて情報を取り出すための強力な道具」として見えるようになります。結果として、ログ解析・入力チェック・テキスト整形などの場面で、狙った通りの抽出がしやすくなります。

match()の書き方(構文)と引数の考え方

javascript+match+regex

JavaScriptのmatch()は、呼び出し自体はシンプルですが「引数に何を渡すか」で挙動が大きく変わります。特にjavascript matchで検索している方がつまずきやすいのは、/.../で直接書くのか、new RegExp()で作るのか、あるいは正規表現以外を渡したときに何が起きるのか、という点です。ここでは構文と引数の考え方を整理します。

パターンを直接書く方法(/pattern/flags)

最も一般的で読みやすいのが、正規表現リテラル/pattern/flagsをそのままmatch()へ渡す方法です。パターンが固定で、コード上に明示できる場合に向きます。

const text = "Order#123 and Order#456";
const result = text.match(/Order#\d+/g); // gは複数マッチを意図するフラグ

正規表現リテラルのメリットは、

  • 見た瞬間に正規表現だと分かる(可読性が高い)
  • エスケープがシンプルになりやすい(文字列に比べて\の二重化が不要)

一方で、パターンを動的に組み立てたい場合(ユーザー入力や設定値で変えたい等)には不向きです。その場合は次のnew RegExpが候補になります。

RegExpオブジェクトで渡す方法(new RegExp)

パターンやフラグを変数から組み立てる必要があるときは、RegExpコンストラクタを使って正規表現オブジェクトを作り、match()に渡します。

const word = "error";     // 例:検索語を変数で受け取る
const flags = "i";        // 大文字小文字を無視
const re = new RegExp(word, flags);

const text = "ERROR: something happened";
const result = text.match(re);

new RegExp()利用時に重要なのは「文字列として渡す」点です。たとえば、バックスラッシュが絡むパターンは、JavaScript文字列側のエスケープも考慮する必要があります。

// \d を使いたい場合、文字列では "\\d" と書く必要がある
const re = new RegExp("\\d+");

また、new RegExp(existingRegExp)のように既存の正規表現から作り直すこともできますが、フラグを上書きするときのルールなどが絡みやすいので、意図が明確な場合に絞るのが安全です。

正規表現以外を渡した場合の挙動(暗黙変換)

match()の引数には通常RegExpを渡しますが、実は正規表現以外(文字列など)を渡した場合、内部的に「正規表現へ暗黙変換」される挙動になります。つまり、次のようなコードも動作します。

const text = "foo.bar";
const result = text.match(".");

ただし、この場合の"."は「ドットそのものを探す」ではなく、正規表現として解釈されるため「任意の1文字」にマッチします。結果として期待と違う動きになりがちです。

同様に、ユーザー入力をそのまま渡すと、*+などのメタ文字が意図せず正規表現として効いてしまうことがあります。正規表現以外を渡せること自体は便利ですが、パターンを「文字列としてそのまま検索したい」意図なら、暗黙変換に頼らず、渡す値を正規表現として安全に扱える形にする(メタ文字をエスケープする等)方針を明確にするのが重要です。

引数を省略した場合の挙動

match()は引数を省略すると、仕様上はundefinedが渡されたのと同じ扱いになります。その結果、暗黙変換の流れで「undefinedという文字列」をパターンにした正規表現として扱われるため、直感とズレた結果になりやすい点に注意が必要です。

const text = "value is undefined";
const result = text.match(); // 引数省略

このケースでは、文字列"undefined"が含まれるかどうかを探すような挙動になり得ます。検索・抽出のつもりでmatch()を使うなら、引数は明示し、

  • 固定パターンなら/pattern/flags
  • 動的パターンならnew RegExp(pattern, flags)

のどちらかに寄せると、レビューでもバグでも追いやすくなります。

RegExp互換オブジェクト(Symbol.match)との関係

match()は「正規表現」だけを受け付けているように見えて、実際にはSymbol.matchという仕組みにより、RegExp互換のオブジェクトを受け取って動作できます。つまり、引数オブジェクトがSymbol.matchメソッドを持っている場合、String.prototype.matchはそれを呼び出す設計です。

const matcher = {
  [Symbol.match](str) {
    // 独自ロジックで「マッチ結果らしきもの」を返せる
    return ["custom-match", str];
  }
};

const text = "hello";
const result = text.match(matcher);

この仕組みのポイントは、match()が「正規表現に限らず、マッチという概念を提供するオブジェクト」に拡張可能であることです。通常の開発ではRegExpを渡すのが基本ですが、ライブラリ設計や特殊な抽出ロジックを共通インターフェースで扱いたい場合に、Symbol.matchが活きることがあります。

match()の戻り値を正しく理解する(超重要)

javascript+regex+match

JavaScriptのmatch()は「マッチしたかどうか」だけでなく、「何が・どこで・どのように」マッチしたかを返します。しかし、正規表現のフラグ(特にg)やキャプチャの有無によって戻り値の形が大きく変わるため、ここを誤解するとバグや例外の原因になります。ここではjavascript matchの戻り値をパターン別に整理して、確実に使い分けられるようにします。

マッチしない場合はnullになる

match()は一致がないとき、空配列ではなくnullを返します。つまり、戻り値を配列として扱う前にnullチェックが必要です。

const text = "abc";
const result = text.match(/\d+/); // 数字は含まれない
console.log(result); // null

この仕様を知らないと、次のように例外になります。

const r = "abc".match(/\d+/);
console.log(r[0]); // TypeError: Cannot read properties of null

安全に扱うには、条件分岐するのが基本です。

const r = "abc".match(/\d+/);
if (r !== null) {
  console.log(r[0]);
}

通常(gなし)で返る配列の中身(全文・グループ・index・input)

gフラグがない通常のmatch()は、「最初にマッチした1件」を配列(Match配列)として返します。配列には以下の情報が入ります。

  • 0番目:マッチした全文(全体一致)
  • 1番目以降:キャプチャグループ(括弧())に対応した文字列
  • index:マッチ開始位置(0始まり)
  • input:元の文字列
const text = "ID: A-123, ID: B-456";
const r = text.match(/ID:\s([A-Z])-(\d+)/);

console.log(r[0]);     // "ID: A-123"  (全文)
console.log(r[1]);     // "A"          (第1グループ)
console.log(r[2]);     // "123"        (第2グループ)
console.log(r.index);  // 0            (開始位置)
console.log(r.input);  // 元の文字列

この「indexinputが付く」のが、gなし戻り値の重要な特徴です。

グローバル(gあり)で返る配列の中身(全マッチ一覧)

gフラグを付けたjavascript matchは挙動が変わり、「一致した全て」を文字列配列として返します。ここでは配列要素はマッチした文字列そのもののみで、indexinput、そしてグループ情報は基本的に含まれません。

const text = "ID: A-123, ID: B-456";
const r = text.match(/ID:\s[A-Z]-\d+/g);

console.log(r); // ["ID: A-123", "ID: B-456"]

よくある混乱として、gありでもr.indexが取れると思ってしまうケースがありますが、通常は取得できません。戻り値の目的が「全マッチの一覧」に絞られるためです。

キャプチャグループがある場合の戻り値

キャプチャグループ(())がある場合、戻り値への反映はフラグにより変わります。

  • gなし:配列の1番目以降にグループが入る(グループ取得ができる)
  • gあり:基本は「全マッチ文字列の配列」になるため、グループは戻り値に出てこない
const text = "A-123 B-456";

// gなし:最初の1件+グループが取れる
const a = text.match(/([A-Z])-(\d+)/);
console.log(a[0]); // "A-123"
console.log(a[1]); // "A"
console.log(a[2]); // "123"

// gあり:全マッチ一覧(グループは含まれない)
const b = text.match(/([A-Z])-(\d+)/g);
console.log(b); // ["A-123", "B-456"]

「複数件を取りつつ、グループも欲しい」場面では、この違いが致命的になりやすいので、戻り値の型を先に意識して設計するのがコツです。

名前付きキャプチャグループ(?<name>)の扱い

名前付きキャプチャグループを使うと、gなしのmatch()では戻り値配列にgroupsプロパティが追加され、名前で取り出せます。番号での取得([1]など)も併用可能です。

const text = "user=alice id=42";
const r = text.match(/user=(?<user>\w+)\s+id=(?<id>\d+)/);

console.log(r[0]);           // "user=alice id=42"
console.log(r.groups.user);  // "alice"
console.log(r.groups.id);    // "42"

一方で、gありの場合は戻り値が「全マッチ文字列の配列」になるため、groupsをこの戻り値から直接参照する形にはなりません(gなしのMatch配列とは性格が異なるためです)。

マッチ位置(indexなど)を参照する方法

マッチ位置を知りたい場合、基本は「gなしのmatch()で返るindex」を参照します。開始位置が0始まりで取れるため、UIのハイライトや切り出し処理に使えます。

const text = "hello world";
const r = text.match(/world/);

console.log(r.index); // 6
console.log(text.slice(r.index, r.index + r[0].length)); // "world"

また、マッチした開始位置から前後を分解するなど、indexを起点にした処理が書けます。

const text = "price: 1200yen";
const r = text.match(/\d+/);

if (r) {
  const before = text.slice(0, r.index);
  const matched = r[0];
  const after = text.slice(r.index + matched.length);

  console.log(before);  // "price: "
  console.log(matched); // "1200"
  console.log(after);   // "yen"
}

このように、javascript matchの戻り値は「一致文字列」だけではなく、状況によっては位置情報やグループ情報まで含むため、gの有無と戻り値の構造をセットで覚えるのが最重要です。

フラグ別の実践パターン(g/i/m/s/y/uなど)

javascript+match+regex

javascript match は、渡す正規表現(RegExp)の「フラグ」によって挙動が大きく変わります。同じパターンでも、取得できる結果の“量”や“解釈のされ方”が変化するため、目的に合ったフラグ選びが実務では重要です。ここでは g/i/m/s/y/u/v/d を中心に、match()での実践的な使い分けを整理します。

g(グローバル)で一致した全てを取り出す

g(global)を付けると、文字列全体から「一致したものをすべて」取り出す用途に向きます。ログから全てのIDを抜く、文中のURLを列挙する、といった場面で定番です。

ただし、match()は g の有無で返り方が変わります。g ありでは「マッチした文字列の配列」が返り、キャプチャグループは原則として返りません(グループも取りたい場合は別の手段が必要になります)。

// 数字の連続をすべて抽出
const text = "ID: 12, 345, 6789";
const result = text.match(/\d+/g);

console.log(result); // ["12", "345", "6789"]

実務上のコツとして、結果は「文字列配列」なので、数値化や重複排除などは後段で配列処理(map/filter/Setなど)を組み合わせる設計になります。

i(大文字小文字無視)で照合する

i(ignoreCase)は、大文字・小文字を区別せずに照合します。ユーザー入力や、表記ゆれ(JavaScript / javascript)が混在しうるテキストを扱う際に有効です。

const text = "JavaScript match is useful.";
console.log(text.match(/javascript/i)); // ["JavaScript"]

注意点として、i は単純なASCIIだけでなく言語特有の大小変換が絡むケースもあります。英字中心の識別子検索には便利ですが、「厳密な区別が必要なトークン」まで無条件にゆるめないように、用途を限定して使うのが安全です。

m(マルチライン)を理解する

m(multiline)は、文字列が複数行のときに ^$ の意味を変えます。m なしでは文字列全体の先頭/末尾にだけ反応しますが、m を付けると「各行の先頭/末尾」にも反応します。

const text = "first line\nsecond line\nthird line";

// mなし: 文字列全体の先頭にしかマッチしない
console.log(text.match(/^second/)); // null

// mあり: 2行目の先頭にもマッチできる
console.log(text.match(/^second/m)); // ["second"]

設定ファイル風のテキストから「行頭のキー」を拾う、複数行ログから「行頭の日時」を検出するなど、行単位の構造を扱うときに効果的です。

s(ドットを改行にマッチ)を理解する

s(dotAll)は、ドット . が改行(\n など)にもマッチするようになります。通常の . は改行を除外するため、複数行にまたがるブロック抽出がうまくいかない原因になりがちです。

const text = "BEGIN\nline1\nline2\nEND";

// sなし: . が改行にマッチしないので失敗しやすい
console.log(text.match(/BEGIN.*END/)); // null(環境やテキスト次第で期待通りにならない)

// sあり: 改行込みでまとめてマッチ
console.log(text.match(/BEGIN.*END/s)); // ["BEGIN\nline1\nline2\nEND"]

「開始タグ〜終了タグ」や「特定の見出し〜次の見出し」など、改行を含む塊を扱うときは s を検討します。.* が広く取りすぎる場合は、非貪欲(.*?)と組み合わせると制御しやすくなります。

y(sticky)で連続マッチさせる考え方

y(sticky)は「現在位置(lastIndex)から必ず連続してマッチする」ことを要求します。これにより、途中をスキップして次の一致を探すのではなく、トークンを先頭から順番に読み取るような挙動に寄せられます。

match()単体では lastIndex を直接操作して“連続走査”を作るのが難しいため、y の本領は「位置を進めながら繰り返し処理する」設計で活きます。とはいえ、stickyの概念を知っていると「なぜここでマッチしないのか(飛ばせないのか)」が理解しやすくなります。

const re = /\d+/y;
const text = "123-456";

// lastIndex=0 からは数字が連続しているのでマッチ
re.lastIndex = 0;
console.log(text.match(re)); // ["123"]

// lastIndex=3 は "-" の位置。stickyはここから数字開始でないと失敗
re.lastIndex = 3;
console.log(text.match(re)); // null

「必ずこの位置から読める形式であるべき」というパーサ的な要件(例:固定フォーマットのトークン列)では、g よりも y が意図に合うことがあります。

u / v(Unicode関連)でハマらないための注意点

Unicodeを含むテキストでは、見た目の「1文字」が内部的に複数のコード単位で表現されることがあります。u(Unicode)フラグは、そうした文字を扱う際の正規表現の解釈をよりUnicodeに沿ったものにします。絵文字や一部の補助平面文字を含む文字列で、. や量指定子の挙動が直感とずれるのを避けたいときに重要です。

// 絵文字などを含むケースを想定
const text = "A😀B";

// uなしだと「1文字」のつもりが想定外の単位になることがある
console.log(text.match(/A.B/));   // 環境や文字により期待とズレる可能性

// uありでUnicodeとして扱う
console.log(text.match(/A.B/u));  // 意図に近づきやすい

また v は、Unicode文字集合の扱いを強化する比較的新しいフラグで、より表現力の高い文字クラス等を扱える方向の拡張です。実務では「環境差(実行エンジン差)」が出やすい領域でもあるため、導入する場合は対象環境での動作確認を前提にし、必要性がある箇所に限定して使うのが無難です。

Unicode周りでハマりやすいポイントは次の通りです。

  • 見た目1文字でも内部表現が複数単位になり、.{n} の数え方が想定と一致しない
  • 結合文字(濁点など)や絵文字のバリエーションで「同じに見えるのに一致しない」ことがある
  • 入力正規化(NFC/NFD)をしていないと照合が不安定になることがある

d(インデックス取得)で位置情報を強化する

d(hasIndices)は、マッチ結果に「どこに一致したか」という位置情報をより豊かに付与します。通常のmatch()でもマッチ開始位置(index)は得られますが、d を使うと各キャプチャグループの開始・終了位置まで追えるため、ハイライト表示や部分置換、エディタ機能のような用途で特に便利です。

const text = "Order: ABC-123";
const re = /([A-Z]+)-(\d+)/d;

const m = text.match(re);
console.log(m[0]);      // "ABC-123"
console.log(m[1]);      // "ABC"
console.log(m[2]);      // "123"

// 位置情報(indices)が取れる
console.log(m.indices); // 例: [[7,14],[7,10],[11,14]] のような形

m.indices には、全体マッチと各グループの [開始, 終了) が入ります。これにより「マッチ文字列そのもの」だけでなく、「元テキストのどこを指しているか」を正確に扱えるようになり、javascript match をUI連携に使う際の表現力が上がります。

よく使う正規表現パターン集(特殊文字・代表例)

javascript+match+regex

javascript matchを実務で使うとき、「どの正規表現を書けばいいか」で手が止まりがちです。この章では、頻出の正規表現パターンを“部品(基本)→注意点(特殊文字)→定番(実務例)”の順で整理します。必要なパーツを組み合わせれば、検索・抽出・軽い検証の入口として素早く形にできます。

文字クラス・量指定子・境界指定の基本

正規表現は、主に「何にマッチさせるか(文字クラス)」「何回繰り返すか(量指定子)」「どこを境目にするか(境界指定)」の3つを組み合わせて作ります。javascript matchでパターンを組む際も、この3要素を押さえると読み書きが一気に楽になります。

カテゴリ記法意味例(用途イメージ)
文字クラス.改行以外の任意の1文字/.+/:1文字以上(ざっくり)
文字クラス\d / \D数字 / 数字以外/\d+/:連続する数字
文字クラス\w / \W単語構成文字(英数字+_)/ それ以外/\w+/:英数字と_の連続
文字クラス\s / \S空白類(スペース・タブ・改行など)/ それ以外/\s+/:連続する空白
文字クラス[abc]a,b,cのいずれか1文字/gr[ae]y/:gray/grey
文字クラス[a-z]a〜zの範囲の1文字/[a-z]+/:小文字英字の連続
文字クラス[^abc]a,b,c以外の1文字(否定)/[^,]+/:カンマ以外の連続
量指定子?0回または1回/https?:\/\//:http/https
量指定子*0回以上/a*/:aが続く限り(0もOK)
量指定子+1回以上/\d+/:数字が1つ以上
量指定子{n}ちょうどn回/\d{4}/:4桁
量指定子{n,}n回以上/\w{8,}/:8文字以上
量指定子{n,m}n〜m回/\d{2,4}/:2〜4桁
境界指定^ / $文字列の先頭 / 末尾/^\d+$/:全体が数字のみ
境界指定\b単語境界(word boundary)/\bcat\b/:catだけ(catalogは除外)

組み立てのコツは、「対象(例:数字)+回数(例:1回以上)+境界(例:行全体)」の順で考えることです。たとえば「数字だけの文字列」を狙うなら、^\d+$のように“両端を固定”すると意図がブレにくく、javascript matchでも結果が安定します。

エスケープが必要な特殊文字一覧

正規表現には「メタ文字(特別な意味を持つ文字)」があり、文字そのもの(リテラル)として扱いたい場合はエスケープが必要です。たとえばドット「.」は任意の1文字を意味するため、「.」そのものを探すには\.と書きます。

文字正規表現での意味リテラルとして探す書き方例(用途)
.任意の1文字\./\.(jpg|png)$/:拡張子のドット
+1回以上\+/\+81/:国番号の+など
*0回以上\*/\*/:記号「*」そのもの
?0回または1回\?/\?/:URLのクエリ開始記号
^先頭\^/\^/:キャレット記号
$末尾\$/\$/:通貨記号など
( )グループ化\( \)/\(\d+\)/:(123)の括弧
[ ]文字クラス\[ \]/\[INFO\]/:[INFO]など
{ }回数指定\{ \}/\{.*\}/:波括弧を含む文字列
|OR\|/a\|b/:区切り記号の|
\エスケープ開始\\/\\/:バックスラッシュ
/(リテラル表記での区切り)\//https?:\/\/.+/:URL

特に混乱しやすいのが「/…/」形式で正規表現を書く場合のスラッシュです。スラッシュをパターン内部で文字として扱いたいなら\/が必要になります。javascript matchでURLやパスを扱うときに頻出なので、最初に押さえておくと事故が減ります。

実務で頻出のパターン例(数字・英字・空白など)

ここでは、現場で「まずこれが欲しい」となりやすい定番パターンをまとめます。目的は、コピペしてすぐjavascript matchに適用できる“叩き台”を持つことです(要件に応じて境界指定や許容範囲を調整してください)。

目的パターン例マッチする例補足
数字の連続を抽出/\d+/注文ID: 12345 → 12345まずはこれ。必要なら桁数を{n,m}で縛る
整数(符号あり)/[+-]?\d+/-12, +3, 99先頭の符号を任意に
英字のみ(小文字/大文字)/[A-Za-z]+/ABCdef日本語を含めない“英字”の抽出に
英数字とアンダースコア/\w+/user_id_01\wは基本的に[A-Za-z0-9_]
空白(スペース/タブ等)の連続/\s+/“a b”の” “整形や分割前処理でよく使う
行頭/行末の空白/^\s+|\s+$/” hello “前後の空白を拾う形(抽出の叩き台)
単語っぽい塊(空白で区切り)/\S+/“a-b c” → “a-b”空白以外の連続=トークンとして便利
ドメインっぽいもの(簡易)/[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)+/example.co.jp“それっぽい抽出”用の簡易版(厳密仕様は要調整)
拡張子(末尾)/\.[A-Za-z0-9]+$/photo.jpeg → .jpeg末尾固定で誤検出を減らす
16進数(#ありの色コード等)/#[0-9A-Fa-f]{6}\b/#1a2b3c境界\bで後続の英数字連結を抑制

実務では「抽出したい文字種」と「どこまでを1まとまりにするか」の2点がブレやすいです。たとえば数字なら\d+、英字なら[A-Za-z]+、空白なら\s+を基本部品として持ち、必要に応じて^$で範囲を固定すると、javascript matchの結果が意図通りになりやすくなります。

文字列抽出の実例:目的別サンプル

javascript+regex+match

JavaScriptのmatch()は、文字列から「欲しい情報だけを抜き出す」場面で特に活躍します。ただし、同じjavascript matchでも目的によって正規表現の書き方やフラグ選びが変わります。ここでは、実務でよくある抽出ニーズを「1件だけ」「複数件」「動的に差し替え」「結果を加工」の4パターンで具体例とともに整理します。

1件だけ抽出する(gなしのmatch)

最初に見つかった1件だけを取り出したいなら、gフラグなしでmatch()を使うのが基本です。例えば、ログ文字列から「ユーザーID」を1つだけ抜き出すケースを考えます。

const text = "userId=U-1024; action=login; userId=U-2048";
const m = text.match(/userId=([A-Z]-\d+)/);

if (m) {
  // m[0] はマッチ全体、m[1] は括弧で取った部分(ここではID)
  const userId = m[1];
  console.log(userId); // "U-1024"
}

ポイントは「最初の1件で止まる」ことです。入力に同じ項目が複数回出てきても、先頭から最初に一致したものだけが欲しい場合に向きます。

  • 設定値やクエリの「代表値」だけ取りたい
  • 最初の見出しや先頭のタグだけ抜きたい
  • 入力が長くても、先頭一致だけ確認できれば十分

複数件を一括抽出する(gありのmatch)

文章中に現れる複数のパターンをまとめて拾いたい場合は、gフラグを付けてmatch()を使います。例えば、テキストからURLっぽい文字列を一括抽出する例です。

const text = "公式はhttps://example.com で、資料はhttp://docs.example.com にあります。";
const urls = text.match(/https?:\/\/[^\s]+/g) || [];

console.log(urls);
// ["https://example.com", "http://docs.example.com"]

gありの場合は「マッチした文字列の配列」が返るため、一覧として扱いやすいのが利点です。マッチしないときはnullになりうるので、上のように|| []で空配列に寄せておくと後続処理が安定します。

  • 文中のメールアドレス、URL、番号、タグなどをまとめて取りたい
  • 抽出結果をそのままリスト化・集計したい
  • 「見つかった数」を数えたい(配列の長さで把握)

正規表現を変数で差し替えて使う(動的パターン)

検索したいキーワードや条件が入力によって変わる場合、正規表現を変数で組み立ててjavascript matchを動的に使うことがあります。例えば「任意のラベル名の値を抜き出す」例です。

function escapeRegExp(s) {
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

const text = "name=Tanaka; role=admin; team=A";
const key = "role"; // ここが動的に変わる想定

const re = new RegExp(`${escapeRegExp(key)}=([^;]+)`);
const m = text.match(re);

const value = m ? m[1] : null;
console.log(value); // "admin"

動的パターンで重要なのは、ユーザー入力など「そのまま正規表現に入れると意味が変わる文字」をエスケープすることです。上のescapeRegExpのように特殊文字を無害化してからRegExpを生成すると、意図しないマッチやエラーを避けやすくなります。

  • 検索対象のフィールド名(key)が可変
  • 入力キーワードを含む行だけ抽出したい
  • 環境別(dev/stg/prodなど)でパターンを差し替えたい

取得結果を加工して使う(配列処理のコツ)

match()の戻り値は、抽出したあとに整形・変換してこそ価値が出ます。ここでは「抽出 → 正規化 → 重複排除」までを短い流れで示します。例として、文章からハッシュタグを抜き出して整形します。

const text = "今日は #JavaScript と #javascript を勉強。#DX も少し。";
const tags = (text.match(/#[\w]+/g) || [])
  .map(t => t.slice(1))        // "#" を外す
  .map(t => t.toLowerCase())   // 大小を正規化
;

const uniqueTags = [...new Set(tags)];
console.log(uniqueTags); // ["javascript", "dx"]

このように、gありで拾った配列をmapで整形し、Setで重複を落とすと、後工程(集計・表示・検索)に使いやすいデータになります。

また、抽出した数値を「数値型に変換して合計」など、次の処理につなげるのも定番です。

const text = "item:120円, item:350円, item:90円";
const nums = (text.match(/\d+(?=円)/g) || []).map(Number);
const sum = nums.reduce((a, b) => a + b, 0);

console.log(nums); // [120, 350, 90]
console.log(sum);  // 560
  • match(...)nullになりうるので、|| []で空配列に寄せる
  • mapで不要文字の除去・大小統一・型変換(Numberなど)を行う
  • 重複排除はSet、集計はreduceが相性良い

match()と他メソッドの使い分け(迷いやすい比較)

javascript+regex+match

JavaScriptで正規表現を扱うとき、「結局どれを使えばいいのか」で迷いやすいのが match() 周辺のメソッドです。結論から言うと、javascript matchString.prototype.match())は“抽出”寄りのメソッドですが、目的が「真偽だけ欲しい」「位置だけ欲しい」「全件+グループも欲しい」などの場合、別メソッドのほうが読みやすく・意図が明確になります。ここでは、混同されがちなメソッド同士を比較し、使い分けの判断軸を整理します。

match()とtest()の違い(真偽判定か抽出か)

match() はマッチした内容(配列やnull)を返すのに対し、test() は「一致するかどうか」の真偽(true/false)だけを返します。つまり、検証(バリデーション等)で“結果の文字列が不要”なら test() がシンプルです。

  • 抽出したいstr.match(/.../)
  • 一致するかだけ知りたい/.../.test(str)
// 一致するかどうかだけ(真偽)
const isPhoneLike = /^\d{2,4}-\d{2,4}-\d{3,4}$/.test("03-1234-5678"); // true/false

// 実際に一致した文字列も欲しい(抽出)
const m = "tel:03-1234-5678".match(/\d{2,4}-\d{2,4}-\d{3,4}/);
if (m) {
  // m[0] にマッチした文字列
}

なお、test()RegExp 側のメソッドである点も違いです。「パターンが主体」なら test()、「文字列から取り出す」なら match() と覚えると迷いにくくなります。

match()とexec()の違い(繰り返し取得を含む)

exec()RegExp.prototype.exec() で、1回の呼び出しで“次の1件”を返します。特に g(グローバル)フラグ付きの場合、正規表現オブジェクトが内部に持つ lastIndex を進めながら、繰り返し取得できるのが特徴です。一方、match() は文字列側のメソッドで、基本的に「結果をまとめて(または1回で)得る」用途が中心です。

  • 1件ずつ順に取りたい(イテレーション制御したい)exec()
  • 文字列からまとめて取りたいmatch()
// exec() で繰り返し取得(gフラグ + lastIndex)
const re = /item-(\d+)/g;
const text = "item-12, item-34, item-56";

let r;
while ((r = re.exec(text)) !== null) {
  // r[0] = "item-12" など
  // r[1] = "12" など(キャプチャグループ)
  // r.index で出現位置も扱える
}

exec() は柔軟ですが、同じ正規表現オブジェクトを使い回すとlastIndexの状態により結果が変わることがあります。繰り返し取得が必要で、状態管理を理解したうえで使うと強力、という位置づけです。

matchAll()との違い(グループを全部取りたい場合)

matchAll() は「全マッチ」を対象にしつつ、各マッチのキャプチャグループ情報も保持したまま列挙できるメソッドです。match()g フラグが付くと“マッチした文字列の配列”になり、キャプチャグループが落ちやすいのに対し、matchAll() は各要素が exec() 相当の結果(グループやindexなどを含む)になります。

  • 全件の一致文字列だけでよいmatch()gあり)
  • 全件についてグループも取りたいmatchAll()
// 例:key=value を全部拾い、key と value も取りたい
const text = "a=1 b=2 c=300";
const re = /(\w+)=(\d+)/g;

// match()(gあり)だと ["a=1","b=2","c=300"] になり、グループが取れない
const onlyAll = text.match(re);

// matchAll() なら、各マッチでグループも取れる
for (const m of text.matchAll(re)) {
  const key = m[1];
  const value = m[2];
  // m[0] は "a=1" など
}

「一覧で取りたい」だけなら javascript match で十分ですが、「後工程でグループを使う(キーと値に分解する等)」なら matchAll() を選ぶとコードの意図が明確になります。

search()・replace()との役割分担

search()replace() は、同じく正規表現を受け取れますが、目的が異なります。match() が“見つかった内容の抽出”に寄るのに対し、search() は“位置検索”、replace() は“置換”に特化しています。

メソッド主目的戻り値のイメージ
match()一致結果の抽出配列 / null
search()最初の一致位置を知るindex / -1
replace()一致箇所を別文字列に置換置換後の新しい文字列
const text = "OrderID: A-12345";

// 位置が欲しいだけなら search()
const pos = text.search(/[A-Z]-\d+/); // 見つからなければ -1

// 抽出したいなら match()
const id = text.match(/[A-Z]-\d+/)?.[0]; // "A-12345" など

// マスキングなど置換したいなら replace()
const masked = text.replace(/\d/g, "*"); // "OrderID: A-*****"

「抽出するなら match()」「位置だけなら search()」「加工して別の文字列を作るなら replace()」という役割分担で考えると、メソッド選択の迷いが減り、可読性も上がります。

互換性と仕様の注意点(ブラウザ差分を避ける)

javascript+regex+compatibility

ECMAScript仕様上の位置づけ(概要)

JavaScriptのmatch()は、ECMAScript仕様において「文字列(String)から正規表現(RegExp)による一致結果を取得するための仕組み」として定義されています。重要なのは、javascript matchという機能が単体で完結しているのではなく、内部的に正規表現側の“プロトコル”に処理を委譲できる点です。

具体的には、String.prototype.matchは引数として渡された値に対し、Symbol.match(正規表現互換のためのシンボル)を参照してマッチ処理を進めます。通常はRegExpインスタンスが渡るため、結果は「RegExpのマッチ動作」に従いますが、Symbol.matchを実装したオブジェクトで挙動を差し替えることも仕様上は可能です。

また互換性の観点では、match()自体は古くから広く実装されている一方で、「どんな正規表現構文が使えるか」「どのフラグや構文が実装されているか」はエンジン(ブラウザのJSエンジン、Node.jsのV8など)の対応状況に左右されます。そのため、仕様として存在していても、実環境の実装差で想定どおりに動かないケースが起こり得ます。

ブラウザ互換性のチェック観点

javascript matchの互換性で躓きやすいのは、match()メソッドそのものよりも「正規表現の新しめ仕様・フラグ・関連プロパティ」にあります。ブラウザ差分を避けるために、チェック観点を分解して確認するのが安全です。

  • 対象環境(ブラウザ/OS)の最低バージョン
    社内端末・組み込みブラウザ・古いiOS Safariなど、更新が止まりがちな環境が混ざると差分が出やすくなります。

  • 正規表現構文の対応状況
    後述するlookbehindのほか、名前付きキャプチャ、Unicode関連の挙動など、構文単位で対応状況が異なります。

  • フラグの対応状況
    近年追加されたフラグ(例:インデックス取得系など)は古い環境で未対応のことがあります。未対応だと構文エラーになる場合もあります。

  • ビルド/トランスパイルの影響
    正規表現はトランスパイルで完全に“同じ意味”へ変換できないケースがあり、ビルド後の成果物が一部環境で壊れることがあります(特に高度な正規表現構文)。

  • テストの粒度(構文エラー / 結果差)
    非対応の構文は実行時に例外(SyntaxError)として落ちることがあり、単体テストで早期に検知できます。一方、実装差により「落ちないが結果が違う」ケースもあるため、境界条件のテストデータを用意して差分を検出します。

実務では、仕様書の理解だけでなく「対象ブラウザにその構文があるか」を必ず確認し、さらに“非対応だった場合にどう劣化させるか”まで含めて設計しておくのが、ブラウザ差分を最小化するコツです。

lookbehind等の新しめ構文が動かない場合の対処方針

lookbehind(後読み)などの新しめ正規表現構文は、未対応環境ではmatch()以前に「正規表現リテラル/RegExp生成の時点で構文エラー」になることがあります。つまり、条件分岐で回避しようとしても、ソースとして読み込まれた時点で落ちる可能性があるため、対処方針は“書き方”から考える必要があります。

代表的な進め方は次のとおりです。

  1. (第一選択)lookbehindを使わない等価・近似パターンに置き換える
    lookbehindは「直前条件を満たす位置にだけマッチさせる」用途で便利ですが、多くの場合は次のような書き換えで回避できます。

    • キャプチャして必要部分だけ取り出す:前提条件をグループで含めてマッチし、欲しい部分をグループとして取得する

    • 境界条件(^/$や区切り文字)を活用する:前後の文字種や区切りを明示して、実質的に同等の範囲を狙う

    この方針は、最も互換性が高く、ビルド工程にも依存しづらいのが利点です。

  2. (第二選択)対応環境を明確化し、非対応環境は別ロジックへフォールバックする
    どうしてもlookbehindが必要な要件(例:置換や抽出条件が複雑で、他の書き換えで可読性・保守性が著しく低下する)では、非対応環境向けに代替実装を用意します。ポイントは、非対応構文をコード上に直接書かないことです。たとえば正規表現を文字列として用意し、対応している環境でのみnew RegExp()で生成するなど、ロード時に構文エラーが出ない形にします。

    // 例:非対応環境で“構文として”落ちるのを避けるため、文字列で持つ
    function safeMatch(input) {
      const patternSource = "(?<=ID:)\\d+";
      try {
        const re = new RegExp(patternSource); // ここで失敗する環境がある
        return input.match(re);
      } catch (e) {
        // フォールバック(lookbehindなしの別手段)
        const fallback = /ID:(\d+)/;
        const m = input.match(fallback);
        return m ? [m[1]] : null;
      }
    }

    このように、機能検出とフォールバックをセットで設計すると、ブラウザ差分があってもアプリ全体が壊れるリスクを下げられます。

  3. (第三選択)ビルド時に変換・制約をかける(ただし限界を理解する)
    正規表現は、コードのトランスパイルと比べて“完全互換な変換”が難しい分野です。ツールである程度の変換ができる場合でも、性能劣化やマッチ結果の差が出ることがあります。そのため、ビルドで吸収する場合は「変換後の挙動をテストで固定する」「対象データ量で性能を確認する」までセットにするのが現実的です。

まとめると、互換性の要点は「match()が使えるか」ではなく「その正規表現が対象ブラウザで“構文として解釈でき、同じ結果を返すか”」です。lookbehindのような新しめ構文を使うほど、非対応時の壊れ方が大きくなるため、回避策(書き換え・フォールバック・ビルド制約)をあらかじめ用意しておくことが、ブラウザ差分を避ける実務的な近道になります。

よくある落とし穴とデバッグのコツ

javascript+regex+debugging

javascript match は一見シンプルですが、正規表現のフラグや入力文字列の性質によって「戻り値の形」「例外の発生」「想定外のマッチ」が起こりやすいポイントがあります。ここでは実務でつまずきやすい落とし穴を3つに絞って、原因とデバッグの観点を整理します。

gフラグで戻り値の形式が変わる問題

match() は g フラグ(グローバル)が付くかどうかで、戻り値の形式が大きく変わります。ここを誤解すると「グループが取れない」「indexがない」などのバグにつながります。

  • g なし:最初の1件だけを返し、配列には(全文に加えて)キャプチャグループ等も入る
  • g あり:一致した全文の一覧だけを配列で返し、キャプチャグループ等は基本的に返らない

例えば、キャプチャグループを使って「キーと値」を取りたいのに、うっかり g を付けてしまうと、欲しい情報が消えたように見えます。

// 例:key=value から value を取りたい
const s = "id=123&type=book";

// gなし:グループが取れる
console.log(s.match(/id=(\d+)/));
// => ["id=123", "123", index: ..., input: ...]

// gあり:一致した全文一覧のみ(グループが取れない)
console.log(s.match(/id=(\d+)/g));
// => ["id=123"]

デバッグのコツは、まず「この match の目的は何か」を言語化することです。

  • 「一致した文字列の一覧が欲しい」なら /.../g の match が自然
  • 「グループ(部分抽出)や位置情報も欲しい」なら g なしを検討(あるいは設計を見直す)

「g を付けたのにグループが取れない」は仕様であり、バグではありません。 戻り値を console.log(result) でそのまま確認し、「配列の要素が何になっているか」を最初に把握すると迷いにくくなります。

nullチェックを忘れて例外になる問題

javascript match の典型的な事故が「マッチしないときは null が返る」ことを忘れて、配列として扱ってしまうケースです。マッチしない入力が混ざると、本番で突然落ちる原因になります。

const s = "abc";
const m = s.match(/\d+/); // 数字がないので null

// 例外:Cannot read properties of null (reading '0')
console.log(m[0]);

対策はシンプルで、null を前提に分岐させます。

const s = "abc";
const m = s.match(/\d+/);

if (m) {
  console.log(m[0]);
} else {
  // マッチしない場合の扱いを明示する
  console.log("数字は見つかりませんでした");
}

「見つからないのは異常なのか、それとも通常ケースなのか」を決めておくと、分岐の設計もブレません。

  • 通常ケースとして起こり得る:null を許容し、代替値やスキップ処理
  • 起こっては困る:null を検知してエラー扱い(ログ・例外など)

デバッグ時は、入力文字列そのものが想定と違うことも多いです。match() の前後で「入力」「正規表現」「戻り値」をセットで出力すると、原因が早く特定できます。

Unicode・改行・貪欲/非貪欲で想定外になる問題

正規表現は、文字の種類(Unicode)や改行の扱い、量指定子の貪欲/非貪欲によって、結果が「取れたり取れなかったり」「取りすぎたり」します。match() 自体の問題というより、正規表現の前提が崩れるのが原因です。

Unicodeで「1文字」のつもりがズレる

絵文字や一部の記号は、見た目は1文字でも内部的には複数コード単位になることがあります。その結果、. や文字数前提のパターンが期待通りにならないことがあります。

  • 「ユーザーが入力した1文字」を想定していたのに、絵文字で崩れる
  • 全角・半角・結合文字などが混ざり、想定外にマッチする/しない

デバッグのコツは、対象文字列に絵文字や全角が混ざっていないか、そしてマッチ対象の「文字の単位」をどう扱うべきかを疑うことです。期待結果とズレる場合、テストデータに Unicode を含めて再現させると切り分けが進みます。

改行を含むと . が止まる/行頭行末がズレる

ログ、HTML断片、複数行テキストなどを match する際は、改行があるだけで挙動が変わることがあります。特に、. が改行にマッチしない前提で書いた正規表現だと「途中で途切れる」現象が起きます。

  • 複数行の入力で、一致が途中で切れて null になる
  • 行単位で取りたいのに、想定以上に広く/狭くマッチする

デバッグでは、まず入力に改行が含まれているかを目視できる形で確認します(例:JSON.stringify(text)\n が見える)。そのうえで「改行をまたいで取りたいのか」「行ごとに取りたいのか」を明確にし、正規表現の前提を合わせます。

貪欲/非貪欲で「取りすぎる」「足りない」

.*.+ のような量指定子は基本的に貪欲(可能な限り長く)にマッチします。例えば区切り文字が複数あるデータで「最初の区切りまで」を取りたいのに、最後の区切りまで飲み込んでしまうことがあります。

// 例:カッコ内を取りたいつもり
const s = "A(1) B(2)";

// 貪欲:最初の "(" から最後の ")" まで取ってしまう
console.log(s.match(/\(.*\)/));
// => ["(1) B(2)"]

// 非貪欲:最短一致にして意図に近づける
console.log(s.match(/\(.*?\)/));
// => ["(1)"]

デバッグのコツは、マッチ結果が「長すぎる」場合に貪欲性を疑うことです。さらに、区切り文字があるなら .* ではなく「区切り文字以外」を指定するなど、取りすぎない設計に寄せると安定します。

まとめると、match() の不具合に見える問題の多くは「入力(Unicode/改行)」「量指定子の性質」「フラグの影響」の組み合わせで起きます。 まず戻り値(null/配列の中身)を観察し、次に入力の特殊性(改行・絵文字・区切りの多さ)を疑うのが近道です。