正規表現は、書き間違えてもエラーにならず、それらしい結果を返すことがあります。テストで使った1つ2つの文字列では正しく動いて見えるので、実際のデータで初めておかしいと気づきます。
この記事では、JavaScript の正規表現で、エラーは出ないのに結果だけ間違う5つのパターンを、Node.js v24.14.1 で実行した結果つきで紹介します。コードは .mjs ファイルに保存して node ファイル名.mjs で実行しています。
1. g を付けた正規表現を使い回すと、同じ文字列でも test が false を返す
const re = /a/g;
console.log(re.test("a"), re.test("a"), re.test("a")); // true false true
同じ文字列 "a" を3回調べただけなのに、2回目だけ false になりました。
g(全部探す)を付けた正規表現は、「前回どこまで調べたか」を lastIndex という値で覚えています。1回目でマッチすると lastIndex が 1 になり、2回目は文字列の1文字目より後ろから探すので見つからず、false を返して lastIndex が 0 に戻ります。3回目はまた先頭から探すので true です。
配列の絞り込みで使うと、結果から一部が抜けます。
const words = ["apple", "avocado", "banana", "apricot"];
const reg = /^a/g;
console.log(words.filter((w) => reg.test(w))); // [ 'apple', 'apricot' ]
console.log(words.filter((w) => /^a/.test(w))); // [ 'apple', 'avocado', 'apricot' ]
a で始まる avocado が、g 付きのほうだけ抜けました。何回目で false になるかは文字列によって変わり、"aa" を3回調べたときは true → true → false でした。test で「含まれているか」を調べるだけなら、g は付けません。
2. replace は、g を付けないと最初の1つしか置き換えない
console.log("2026-10-10".replace("-", "/")); // 2026/10-10
console.log("2026-10-10".replace(/-/g, "/")); // 2026/10/10
console.log("2026-10-10".replaceAll("-", "/")); // 2026/10/10
文字列を渡した replace は、最初に見つかった1か所だけを置き換えます。g を付けない正規表現(/-/)を渡しても同じで、結果は 2026/10-10 でした。全部置き換えるときは、g を付けた正規表現を渡すか、replaceAll を使います。
replaceAll に正規表現を渡すときは g が必要です。付けないと、ここだけはエラーになりました(この行はエラーで止まるので、試すときは単独で実行してください)。
"a.b.c".replaceAll(/\./, "-");
// TypeError(Node.js での表示): String.prototype.replaceAll called with a non-global RegExp argument
3. . は改行にマッチしない
const text = "開始\n本文\n終了";
console.log(/開始.*終了/.test(text)); // false
console.log(/開始.*終了/s.test(text)); // true
console.log(/開始[\s\S]*終了/.test(text)); // true
. は「どの1文字でも」という意味ですが、改行は含みません。テキストエリアに入力された複数行の文章を . で行をまたいで調べると、1行のときは動いていたのに、改行が入った途端にマッチしなくなることがあります。
改行もまたいで探すときは、s フラグを付けるか、[\s\S](空白か空白以外=どの文字でも)を使います。
4. + や . を、そのままの文字として書いている
console.log(/1+1/.test("1+1=2")); // false
console.log(/1\+1/.test("1+1=2")); // true
+ は正規表現では「直前の文字の1回以上の繰り返し」なので、/1+1/ は「1が1回以上続いて、そのあとに1」= 11 や 111 を探します。"1+1" という文字そのものを探すなら、\+ のように \ を付けます。
. も同じで、/1.5/ は "1.5" にも "1x5" にもマッチしました。
console.log("1x5".match(/1.5/)?.[0]); // 1x5
console.log("1x5".match(/1\.5/)); // null
利用者が入力した文字で正規表現を作るときは、記号を \ 付きに変えてから使います。記号の並び方によっては、new RegExp("C++")(Node.js での表示: SyntaxError: Invalid regular expression: /C++/: Nothing to repeat)や new RegExp("a(b")(同: Invalid regular expression: /a(b/: Unterminated group)のように、エラーで止まることもありました。Node.js v24 と、手元の WebKit(Safari と同じエンジン)では RegExp.escape が使えました。
const userInput = "a.b";
console.log(new RegExp(userInput).test("axb")); // true(. がどの文字でもになっている)
console.log(new RegExp(RegExp.escape(userInput)).test("axb")); // false
なお、RegExp.escape("a.b") を表示すると \x61\.b になりました。先頭の英字も別の書き方に変わりますが、意味は同じ a.b という文字そのものです。
RegExp.escape は新しい機能です。MDN の互換性データでは Chrome 136、Firefox 134、Safari 18.2、Node.js 24.0.0 から使えるとされています。古い環境も対象にするなら、記号を \ 付きに置き換える関数を自分で用意します。
const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
console.log(new RegExp(escapeRegExp(userInput)).test("axb")); // false
5. \w と \d は、日本語と全角数字にマッチしない
console.log(/^\w+$/.test("tanaka_1")); // true
console.log(/^\w+$/.test("田中")); // false
console.log("名前: 田中太郎".match(/\w+/g)); // null
console.log(/^\d+$/.test("123")); // true
console.log(/^\d+$/.test("123")); // false
\w は半角の英字・数字・_ だけで、ひらがなや漢字、全角の英字(ABC)は含みません。\d も半角の 0〜9 だけです。「名前は文字だけ」「数字だけ」のつもりで入力チェックを書くと、日本語の名前や全角数字ではじかれます。
日本語も含めたいときは、u フラグを付けて、文字の種類で指定します。
console.log(/^\p{Script=Han}+$/u.test("田中")); // true(漢字だけ)
console.log(/^[\p{L}\p{N}_]+$/u.test("田中_1")); // true(文字・数字・_)
console.log(/^\p{Nd}+$/u.test("123")); // true(全角やほかの言語の数字も含む)
\p{Nd} は、アラビア文字の数字 ٣ なども通しました。Number("٣") は NaN になるので、数値として計算するなら注意が必要です。半角と全角の数字だけを通すなら、範囲をそのまま書きます。
console.log(/^[0-90-9]+$/.test("123")); // true
console.log(/^[0-90-9]+$/.test("٣")); // false
console.log("123".normalize("NFKC")); // 123(全角の数字を半角にそろえる)
全角数字を通すかどうかは、入力チェックの目的によって決めます。受け取ったあとに数値として計算するなら、normalize("NFKC") で半角にそろえてから調べる方法もあります。
AI が書いた正規表現を見るときは
-
gが付いた正規表現を、変数に入れてtestで何度も使っていないか - 文字列や
gなしの正規表現を渡したreplaceで、全部置き換えたつもりになっていないか - 複数行の文章を
.で調べていないか -
+.?などの記号を、そのままの文字のつもりで書いていないか -
\wや\dで、日本語や全角の入力をはじいていないか
エラーで止まれば気づけますが、ここで挙げた書き方の多くは、エラーにならずに結果だけが変わります。「マッチするはずの文字列」と「マッチしてはいけない文字列」を両方用意して実際に試すと、見落としにくくなります。
未経験者向けの講座を運営しています
未経験から Next.js + Supabase + Claude Code で Webアプリを公開するまで を、全24セッションで体系化した教材です。Claude Code を学習パートナーにする CLAUDE.md と学習モード(learner / developer)の設計までセットで含みます。
- ▶ ライブデモ(インストール不要・無料で今すぐ見られます)→ https://trial-web-ayies128s-projects.vercel.app/?ref=qiita
- 教材完全版+月5,500円のメンタリング(全24セッション+チャット質問し放題)→ https://menta.work/plan/20251?ref=qiita
- 無料体験版(git clone して自分の手元で動かす・最初の数セッション分・⭐ Star もよろしくお願いします)→ https://github.com/ayies128/next-ai-camp-trial
- YouTube『AIエンジニア情報局』(AI×開発ニュースを1本5分でキャッチアップできる別運営チャンネル・無料)→ https://www.youtube.com/channel/UC1rXVD9WYsQPQEWZyd-A1KA/?ref=qiita
※ 中上級者には易しすぎる内容なので、初心者の知り合いへの紹介や社内研修の参考としてどうぞ。