正規表現は強力ですが、「だいたいこう書けば動く」で書くと静かに事故ります。手元のテストは通ったのに本番の入力で取りこぼす、全角やマルチバイトで急に一致しなくなる——原因の多くは、メタ文字やフラグの既定の挙動を思い込みで誤解していることです。ここでは実務でつまずきやすい落とし穴を、入力・意図した式・実際の結果・修正版の形で並べて確認します。
入力・意図した式・実際の結果・修正版
下の表はすべて JavaScript(ECMAScript)の正規表現で実際に挙動を確認したものです。列は「何をしたいか(目的)/書いた式(パターン)/与えた入力/実際の結果/修正版(対策)」です。
| 目的 | パターン | 入力 | 結果 | 対策 |
|---|---|---|---|---|
<a> だけ取り出す | <.+> | <a><b> | 全体 <a><b> にマッチ(貪欲) | 非貪欲 <.+?> → <a> |
| 改行をまたいで任意1文字 | /a.b/ | a\nb(\n=改行) | 不一致 | s(dotAll)→ /a.b/s で一致 |
| 数字にマッチ | \d | 全角 123 | 不一致(ASCII数字のみ) | 半角へ正規化してから \d |
| 「文字」だけか判定 | ^\w+$ | 日本語 | 不一致(\w=[A-Za-z0-9_]) | /^\p{L}+$/u なら一致 |
| 各行の先頭/末尾で判定 | /^\w$/g | a\nb | マッチなし(文字列全体の先頭/末尾扱い) | m を追加 /^\w$/gm → ["a","b"] |
リテラルの . にマッチ | [.] | .(と a) | . に一致・a に不一致 | クラス内はほとんどのメタ文字が無力化=ドット自体を安全に表せる(- ^ ] は例外) |
cat か dog に完全一致 | /^cat|dog$/ | "catalog" / "hotdog" / "dogma" | true / true / false((^cat)|(dog$) と解釈) | 交替をグループで囲む /^(cat|dog)$/("hotdog"→false) |
| 同じ式で繰り返し判定 | const re=/x/g; re.test("x") | "x" を2回 | 1回目 true・2回目 false(lastIndex が進む) | 毎回リテラルを作る/g を外す(.test() に g は不要) |
| 1文字だけか判定(絵文字) | /^.$/ | "😀" | false(😀 は2コード単位) | u フラグ /^.$/u → true |
すべての a を置換 | "a-a-a".replace(/a/,"b") | "a-a-a" | "b-a-a"(最初の1件のみ) | g を付ける /a/g → "b-b-b"(または replaceAll) |
とくに事故りやすいポイント
最上段の貪欲マッチはもっとも多い事故です。+ や * は既定でできるだけ長く飲み込むため、最初の > で止まってほしくても行末まで一気にマッチします。? を付けて非貪欲(最短一致)にすれば <a> だけを取り出せます。
全角数字や日本語も定番です。\d と \w は既定でASCIIだけを見るため、全角 123 や 日本語 は素通りします。文字種を厳密に扱いたいときは、入力を半角へ正規化するか、u フラグと Unicodeプロパティ \p{L} のように書きます。^/$ のアンカーも誤解されがちで、既定では文字列全体の先頭/末尾を指します。複数行のうち各行を対象にしたいときは m フラグが必要で、"a\nb".match(/^\w$/gm) は ["a","b"] を返します。
ブラウザ内で試せる
これらは頭で考えるより手を動かすのが早いです。正規表現テスターにパターンと入力を貼れば、マッチ範囲・キャプチャ・フラグの効き方をその場で確認できます(入力はブラウザ内だけで処理され、どこにも送信しません)。一致した箇所をまとめて書き換えたいときはテキスト置換が便利です。
よくある質問
. はなぜ改行に一致しないの?
s(dotAll)フラグを付けると、. が改行も含む任意の1文字になります。/a.b/ は a\nb に不一致ですが、/a.b/s なら一致します。\w が日本語に効かないのは?
\w は [A-Za-z0-9_] と同義で、ASCIIの英数字とアンダースコアだけを指すからです。日本語を含めて「文字」を判定したいなら、u フラグと Unicodeプロパティを使い /^\p{L}+$/u のように書きます。挙動はJavaScript(ECMAScript)の正規表現で確認しています。言語・エンジンにより差があります(2026-07-16時点)。