正規表現テスター
期待される結果: 2026-10-02 と 2026-11-15 の2件が一致します。置換結果は Releases: 02/10/2026, 15/11/2026. です。
制限事項: PCREではなくJavaScript RegExpを使用します。このパターンは日付の形式を探すだけで、暦上の正しさは検証しません。Workerの実行時間は1.5秒までです。
入力した内容が保存・記録・共有されることはありません。
パターンを入力すると、マッチした箇所がリアルタイムでハイライトされ、キャプチャグループと位置も確認できます。照合はバックグラウンドスレッドで実行されるため、破滅的バックトラッキングが起きてもページは固まりません。
使い方
- パターンを入力し、必要なフラグを選びます。
- テスト対象のテキストを貼り付けると、その場でハイライトされます。
- 「置換」を有効にすると、$1 や $<name> を使った置換結果を確認できます。
正規表現にできないこと
正規表現が照合するのはパターンであって構造ではありません。何かの内側にどれだけ深く入っているかを記憶できないため、任意の入れ子を持つものは表現できる範囲をはっきり超えています。対応する括弧、入れ子の引用符、HTML、JSON、ソースコードがそうです。
こうしたものにうまく動いているように見えるパターンは、よくあるケースに一致しているだけで、それ以外ではたいてい黙って失敗しています。属性に>が含まれるまでは動くHTMLタグのパターン。文字列リテラルの途中を食べてしまうコメント除去。引用符で囲まれた最初のカンマで壊れるCSVの分割。どれも、何か月も経ってから他人のデータで表面化するバグです。
目安は、照合したいものが自分自身を含みうるならパーサーを使う、ということです。正規表現が真価を発揮するのはその一段下の層です。フィールドの検証、既知のログ形式からの値の抽出、コードベース全体での検索と置換、各部分の中に現れない区切り文字での分割などです。
最長一致、最短一致、そしてバックトラックの原因
量指定子は既定で最長一致(貪欲)です。.* はできるだけ多く取り込み、残りのパターンが一致するまで1文字ずつ返していきます。そのため <.+> を <b>text</b> に適用すると、最初のタグではなく文字列全体に一致します。貪欲な .+ がすべてを飲み込み、最後の > が入力の最後の > で満たされるからです。
疑問符を付けると量指定子は最短一致(怠惰)になり、<.+?> はできるだけ少なく取って <b> だけに一致します。たいていの人が意図しているのは最短一致ですが、多くの場合はどちらよりも文字クラスのほうが優れています。<[^>]+> は本当に欲しいものをそのまま表し、タグの境界を越えることがなく、バックトラックもしません。
この最後の点が、実務上気にすべき理由です。パターンを遅くするのはバックトラックであり、(a+)+ や (\s*\w+)* のように量指定子を別の量指定子の中に入れ子にすると、エンジンが探索する経路の数が指数関数的に増えます。ほぼ一致する入力に対しては、そうしたパターンが何分も走り続けることがあります。このテスターがワーカーで照合を行い、1.5秒で打ち切るのは、まさにそうしたパターンをうっかり書いてしまうのがごく普通のことだからです。
痛い目を見る方言の違い
ここで使っているエンジンはブラウザのものなので、結果はJavaScriptと完全に一致します。しかし、パターンを貼り付ける先の言語と一致するとは限りません。
名前付きグループはJavaScript、.NET、最近のPCREでは (?<name>...) ですが、Pythonは (?P<name>...) も受け付け、古いツールは後者しか理解しません。後読みは現在のJavaScript、.NET、PCREで使えますが、Pythonでは固定長でなければならず、Goにはまったくありません。GoのRE2とRustのregexクレートは、線形時間を保証するために、後方参照と先読み・後読みを設計上あえて省いています。どちらかに依存するパターンは、そもそもコンパイルできません。
略記クラスの意味も変わります。JavaScriptの \d はASCIIの数字だけですが、.NETでは既定であらゆる文字体系の数字に一致し、Pythonではパターンがstrかbytesかで変わります。またJavaScriptのuフラグは、. や量指定子をコードユニットではなくコードポイント単位で動作させるもので、これがないとパターンは絵文字や基本多言語面(BMP)外の文字で壊れます。
重要なパターンを本番に出す前に、自分の実行環境で必ずテストしてください。
後から読んでもわかるパターンを書く
アンカーを付けましょう。^ と $ のないパターンでフィールドを検証すると、部分文字列にも喜んで一致します。アンカーのないメールアドレスのパターンは、中に有効なアドレスが埋まっていれば何でも受け入れてしまいます。これは入力検証がすり抜けられる原因として繰り返し現れます。
どちらでも書ける場合は、選択(|)より文字クラスを使いましょう。[abc] は (a|b|c) より明快で速く、[^,]+ は「カンマを越えない」ことを、.+? では近似にしかならない形で正確に表します。グループは数えるのではなく名前を付けましょう。(?<year>...) は前に別のグループが挿入されても壊れませんが、$3 は壊れます。
そして、一致してはいけない入力をテストしましょう。ほとんどのパターンは、一致すべき入力を試しながら一致するまで書き直すという方法で作られ、その結果は決まって緩すぎるものになります。より役に立つのは、一致してはいけない入力を貼り付けて、実際に一致しないことを確認する作業です。形式の崩れたアドレス、区切り文字を含む値、期待するログ形式にほぼ合っているのに少しだけ違う行などです。
実際のパターンを一つずつ読み解く
よくあるログ行を対象にしたパターンを見てみます。^(\S+) \S+ \S+ \[([^\]]+)\] "(\w+) ([^"]*)" (\d{3}) (\d+)$ です。一見すると詰め込みすぎに見えますが、きれいに分解できます。先頭のキャレットは行頭に固定します。(\S+) は空白以外の文字の連なり、つまりクライアントのアドレスをキャプチャし、続く2つの名前なしの \S+ は誰も使わない identity フィールドと user フィールドを読み飛ばします。
\[([^\]]+)\] はタイムスタンプをキャプチャします。エスケープした角かっこ、閉じ角かっこ以外のすべての文字、そして閉じ角かっこ、という組み立てです。ここで頑丈さを生んでいるのは否定文字クラスで、.+ のようにフィールドの終わりを越えて食い込んでしまうことがありません。引用符で囲まれたリクエスト部分も同じ考え方を引用符に当てはめたもので、メソッドとパスに分けてあります。
(\d{3}) はステータスコードとしてちょうど3桁をキャプチャし、(\d+) はバイト数をキャプチャして、ドル記号が行末を固定します。ここから一般的な教訓が得られます。否定文字クラスと明示的な回数指定で組み立てたパターンは、ドットと最短一致の量指定子で組み立てた同じパターンよりも読みやすく、動作も速いということです。
破滅的バックトラッキングを避ける
バックトラッキング方式のエンジン、つまり大半のエンジンは、マッチに失敗すると別の可能性を試します。量指定子を入れ子にすると、入力の分け方が指数関数的な数だけ生まれるため、(a+)+$ のようなパターンに a が長く続いたあと b が来る文字列を与えると、宇宙の年齢より長く走り続けることさえあり得ます。
これは ReDoS として知られる現実のサービス拒否攻撃の手口で、実際に本番サービスを停止させてきました。リクエストを解析する経路にある正規表現に細工した入力を与えると、CPUコアを延々と占有し続けるのです。2019年の Cloudflare の世界規模の障害は、まさにこの形のパターンが原因でした。
防御策は3つでほぼ足ります。量指定子を入れ子にせず、ドットより否定文字クラスを選ぶこと。[^"]* は後続の要素と重なり得ないので、バックトラックする余地がありません。エンジンが対応しているならアトミックグループや所有的量指定子を使うこと。そしてパターンがユーザーから与えられる場合は、このテスターのようにタイムアウト付きのワーカーで実行するか、RE2 のような線形時間のエンジンを使うことです。
Unicode と、\w だけでは足りない理由
短縮文字クラスは ASCII を前提に定義され、今もおおむねそのままです。JavaScript では \d は 0-9 にしかマッチせず、\w は ASCII の英字と数字とアンダースコアにしかマッチしません。そのため \w+ で名前を検証するパターンは、José や Zoë をはじめ、ラテン文字で書かれないすべての名前をはじいてしまいます。
これを正しく解決するのが Unicode プロパティエスケープです。u フラグを付ければ、\p{L} はあらゆる文字体系の文字に、\p{N} はあらゆる数値文字に、\p{Script=Han} は特定の文字体系にマッチします。文字クラスを手作業で広げるよりはるかに正確で、Unicode に文字が追加されても動き続けます。
u フラグを付けるべき理由はもうひとつあります。付けないとエンジンはコードポイントではなく UTF-16 のコードユニット単位で動くため、ドットが絵文字の半分にマッチしたり、文字クラスがサロゲートペアを分断したりします。付けたとしても、目に見える1文字が複数のコードポイントからなることはあり、国旗や家族の絵文字、基底文字と結合記号で書かれたアクセント付き文字などがそうです。ですから正規表現で文字数を数えるには、結局 v フラグの集合記法か、書記素を理解するライブラリが必要になります。
正規表現を使うべきでない場面
固定文字列が相手なら、普通の文字列メソッドのほうが明快で高速です。startsWith、includes、split があります。文字列が .json で終わるかを調べるためにパターンを持ち出すのは、直しておきたい癖です。素朴な書き方なら、あとから説明する必要がないからです。
入れ子があるもの、つまり HTML、JSON、ソースコード、対応の取れた括弧などにはパーサーを使ってください。正規表現は深さを数えられないので、これらを扱えているように見えるパターンはどれも、よくあるケースに当たっているだけで、それ以外では黙って失敗しています。HTML を正規表現で解析することについての有名な Stack Overflow の回答が笑えるのは、その根底にある指摘が正確だからです。
構造化されたデータには、たいてい専用の道具があります。引用符の中のカンマを理解する CSV パーサー、userinfo とポートを理解する URL パーサー、タイムゾーンを理解する日付ライブラリです。どれも存在している理由は同じで、正規表現版が何千回も書かれ、そのたびに微妙に間違っていたからです。
繰り返し使うことになるパターン
空白の整理と正規化。\s+ をスペース1つに置換すれば連続した空白がまとまり、trim 関数のない言語では ^\s+|\s+$ で前後を削れます。引用符で囲まれた値の抽出には "([^"]*)" を使います。フィールドの内部には現れない区切り文字で分割するときは、最短一致のドットではなく否定文字クラスを使いましょう。
一方、書かないほうがよいパターンが2つあります。ひとつはメールアドレスを正規表現で検証することで、これは落とし穴です。RFC 5322 の文法は本当に複雑で、現実的な確認はローカル部が空でないこと、アットマークがあること、ドメインにドットがあることを見て、そのうえで確認メールを送ることです。それが唯一の本当の検証になります。URL の検証も同じ形の問題を抱えていますが、今ではどの言語にも、その問いにきちんと答えてくれる URL パーサーが備わっています。
説明が必要なほど長いパターンでは、名前付きグループを使い、対応している言語ならパターン内に空白とコメントを書ける拡張フラグを使ってください。半年後に読めるパターンは、今日2行余計に書く価値があります。
よくある質問
- 破滅的バックトラッキングとは何ですか?
- 一部のパターンは「ほぼマッチする」入力に対して指数時間を要します。典型的なのは量指定子の入れ子で、たとえば (a+)+$ を a の連続+別の文字に対して適用すると、エンジンがあらゆる分割を試すため、数秒から数分CPUが占有されることも珍しくありません。
- なぜこのツールでは固まらないのですか?
- 実行中の正規表現はJavaScriptから中断できないため、照合をWeb Workerで実行しています。1.5秒を超えるとWorkerを終了させ、新しいWorkerに置き換えます。そのためページは応答を保ち、何が起きたかも表示されます。
- フラグの意味を教えてください。
- g は最初の1件だけでなく全件を検索、i は大文字小文字を無視、m は ^ と $ を行頭・行末にも一致させ、s は . を改行にも一致させます。u は \p{...} を含む完全なUnicode処理を有効にし、y は現在位置に固定して照合します。
- 置換でキャプチャグループを参照するには?
- 番号付きグループは $1、$2 のように、名前付きは $<name>、マッチ全体は $&、ドル記号そのものは $$ で参照します。
- 自分のコードと同じエンジンですか?
- ブラウザのJavaScriptエンジンを使用するため、JavaScriptとは完全に一致します。PCRE・Python・Go・.NET は後読みのサポート、名前付きグループの構文、Unicodeの扱いが異なるため、実際の実行環境でも確認してください。
- テストデータは送信されますか?
- いいえ。パターンもテキストもブラウザ内に留まるため、ログや本番データのサンプルも安全に扱えます。
最終更新:
参考資料
MDN — JavaScript RegExp最近の変更
- — 実行できるサンプル、期待される結果、制限事項、参考リンクを追加しました。
操作手順、期待した結果、実際の結果を記載してください。秘密情報や個人情報は削除してください。
