正規表現チートシート:開発者に必要なパターン集

10 min2026年5月13日

なぜ正規表現チートシートが必要か

ネットに転がっている正規表現チートシートの多くは構文の一覧です。アンカー、量指定子、文字クラスの説明はあっても、実務でコピペするパターンは載っていません。このガイドは違います。タスク別に整理しています:メールの検証、URLの抽出、パスワードルールの適用、ログ行の解析。各パターンには動作理由と、より重要な「どこで壊れるか」の説明を付けています。

正規表現の構文は1994年のPerl 5からほぼ変わっていません。基本はJavaScript、Python、Go、Java、.NETのどれでも同じです。違いはフラグ(JavaScriptは/g、Pythonはre.DOTALL)、後読みのサポート(GoのRE2エンジンは後読み非対応)、Unicode処理にあります。本チートシートはWeb開発者が最初に出会うJavaScriptの正規表現構文を使いますが、パターン自体はPCRE互換のエンジンであればどこでも動きます。

最初に一つ意見を述べます。正規表現が約80文字を超えたら、パーサーか複数の単純な正規表現の連鎖に切り替えた方がいいでしょう。300文字のメール検証正規表現をデバッグした経験がありますが、それでもエッジケースを取りこぼしていました。巧妙さより可読性が重要です。

基本構文クイックリファレンス

文字: .は改行以外の任意の1文字にマッチ。\dは数字[0-9]にマッチ。\wは英数字とアンダースコア[a-zA-Z0-9_]にマッチ。\sは空白文字(スペース、タブ、改行)にマッチ。大文字版(\D、\W、\S)はそれぞれの反対にマッチします。バックスラッシュで特殊文字をエスケープ:\.はリテラルのドットにマッチします。

量指定子: *はゼロ回以上。+は1回以上。?はゼロか1回。{3}はちょうど3回。{2,5}は2〜5回。{3,}は3回以上。デフォルトでは量指定子は貪欲(可能な限り多くマッチ)です。?を追加すると怠惰になります:.*?は可能な限り少なくマッチします。HTMLの解析や引用符で囲まれた文字列の抽出で、この区別が重要になります。

アンカーと境界: ^は文字列の先頭(/mフラグで行頭)にマッチ。$は文字列の末尾(/mフラグで行末)にマッチ。\bは単語境界(\w文字と\W文字の間の位置)にマッチ。部分一致を避けるために\bを使います:/\bcat\b/は「cat」にマッチしますが「concatenate」にはマッチしません。

グループと選択: (abc)はキャプチャグループ。(?:abc)はキャプチャしないグループ(マッチ配列を汚さずにグループ化できる)。a|bはaまたはbにマッチ。(?=abc)は肯定先読み(「abc」の前の位置にマッチし、消費しない)。(?<=abc)は肯定後読み(すべてのエンジンでサポートされているわけではない)。

// クイックリファレンスの例
/\d{3}-\d{4}/        // "555-1234" — 電話番号の一部
/\b\w+@\w+\.\w+\b/  // 簡易メール(本番では使わないこと)
/^https?:\/\//       // http:// または https:// で始まる
/(?<=\$)\d+\.\d{2}/ // $記号の後の "19.99"(後読み)
/"([^"]*)"/.exec(str)[1]  // 引用符の中身を抽出

メールアドレスの検証(現実的なアプローチ)

RFC 5322に完全準拠してメールアドレスを検証する正規表現は6,000文字以上あります。使わないでください。実務では明らかなタイプミスをキャッチしつつ、有効だが珍しいアドレスを拒否しないパターンが必要です。推奨パターン:/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/ — [email protected]の形式でスペースがないことを確認するだけです。

なぜこれほどシンプルなのか?有効なメールアドレスには+記号([email protected])、ローカル部のどこにでもドット、引用文字列("weird@chars"@example.com)、さらにはIPアドレスのドメイン(user@[192.168.1.1])が含まれ得るからです。「悪い」アドレスを拒否するほど厳格な正規表現は、有効なアドレスも一部拒否してしまいます。

推奨するアプローチ:正規表現で形式を緩やかに検証し、実際に存在するかは確認メールを送って確かめます。すべてのまともなサービスがそうしています。正規表現は「user@gmailcom」(ドット欠落)や「user@@gmail.com」(@重複)のようなタイプミスを捕捉する第一関門です。

注意点:HTML5のinput type="email"は独自の検証正規表現(WHATWG仕様で定義)を使用しており、RFC 5322とは異なります。バックエンドの正規表現がブラウザのものより厳格だと、フォームは送信成功するのにサーバー側でエラーになる、という現象が起きます。両方をテストしてください。

URLとパスのパターン

自由テキスト中のURL検出は驚くほど困難です。シンプルなアプローチ /https?:\/\/[^\s]+/ はほとんどの場合に機能しますが、括弧を含むURL(Wikipediaのリンクで一般的)で失敗し、末尾の句読点もマッチしてしまいます("Visit https://example.com."のピリオドまで取り込む)。改良版:/https?:\/\/[^\s<>"]+[^\s<>".,;:!?)]/ — 一般的な末尾句読点を除外します。

ユーザーがフォームに入力したURLを検証する場合、正規表現を使わないでください。URLコンストラクタを使います:try { new URL(input) } catch { /* 無効 */ }。ポート、認証情報、フラグメント、IDNドメインなど、合理的な正規表現ではカバーできないエッジケースを処理してくれます。正規表現はテキストからURLを抽出するためのもので、検証するためのものではありません。

ファイルパスのパターンはOS間で異なります。Windows:/^[A-Z]:\\(?:[^\\/:*?"<>|]+\\)*[^\\/:*?"<>|]*$/はC:\Users\docs\file.txtのようなパスを検証します。Unix:/^\/(?:[^\/]+\/)*[^\/]+$/はシンプルです(不正な文字が少ないため)。実務では、nullバイトとパストラバーサル(../)のチェックが重要です。これらがセキュリティに関わる検証です。

クエリパラメータの抽出:/[?&]([^=]+)=([^&]*)/に/gフラグを付ければキーバリューペアが取得できます。ただし、JavaScriptではURLSearchParamsの方が適切です。正規表現はログやURLを含むテキストを解析する場合に使い、実際のURLオブジェクトが利用可能な場合は避けましょう。

// テキストブロックからすべてのURLを抽出
const urlPattern = /https?:\/\/[^\s<>"]+[^\s<>".,;:!?)]/g;
const text = "詳細は https://example.com/path?q=1 または http://test.org をご覧ください。";
text.match(urlPattern);
// ["https://example.com/path?q=1", "http://test.org"]

// URL検証(正規表現は使わない)
function isValidUrl(str) {
  try { new URL(str); return true; }
  catch { return false; }
}

// パスセグメントの抽出
"/api/v2/users/123/posts".match(/\/([^\/]+)/g);
// ["/api", "/v2", "/users", "/123", "/posts"]

パスワード強度の正規表現チェック

パスワード検証は正規表現が真価を発揮する場面です。構造の解析ではなく、文字クラスの存在確認だからです。典型的な「8文字以上、大文字1つ、小文字1つ、数字1つ、特殊文字1つ」は4つの先読みで表現できます:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-zA-Z\d]).{8,}$/

個人的には1つの巨大な正規表現より個別チェックを推奨します。「数字が必要です」のような具体的なフィードバックを出す方が、一般的な「パスワードが弱すぎます」よりも優れたUXです。各要件を独立して確認します:/[a-z]/.test(pw)で小文字、/[A-Z]/.test(pw)で大文字、/\d/.test(pw)で数字、pw.length >= 12で長さ。結果はアプリケーションロジックで結合します。

NIST 800-63Bガイドライン(2024年更新)は実は複雑性ルールに反対しています。最低8文字、漏洩パスワードリストとの照合、最大64文字まで許容を推奨し、特殊文字の強制や大文字の必須化は不要とします。研究によると、複雑性ルールは予測可能なパターン("Password1!")を生みがちで、長さだけの方がエントロピーが高くなります。

正規表現ではチェックできないことが1つあります。パスワードが漏洩データベースに存在するかどうかです。ハッシュ化してHave I Been PwnedのAPI(k-anonymityモデル、SHA-1ハッシュの先頭5文字だけ送信)と照合する必要があります。「Correct Horse Battery Staple」を弱いと検出できる正規表現は存在しません。辞書チェックだけがそれを行えます。

// オプションA: 先読みを使った単一の正規表現
const strongPassword = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-zA-Z\d]).{8,}$/;
strongPassword.test("Tr0ub4dor&3"); // true

// オプションB: 個別チェック(推奨 — UXが良い)
function checkPassword(pw) {
  return {
    hasLower: /[a-z]/.test(pw),
    hasUpper: /[A-Z]/.test(pw),
    hasDigit: /\d/.test(pw),
    hasSpecial: /[^a-zA-Z\d]/.test(pw),
    longEnough: pw.length >= 12,
    // NISTは最低8を推奨、個人的には12を推奨
  };
}

// よくあるパターンのチェック(漏洩DBの代替にはならない)
const commonPatterns = /^(password|123456|qwerty|admin)/i;

ログ解析とデータ抽出

ここが正規表現の真骨頂です。構造化されたログ行の解析は完璧なユースケースです。フォーマットが予測可能で、データはテキストであり、特定のフィールドを抽出する必要があるためです。Apacheアクセスログ行のパターン:/^(\S+) \S+ \S+ \[([^\]]+)\] "(\S+) (\S+) \S+" (\d+) (\d+)/で、IP、タイムスタンプ、メソッド、パス、ステータスコード、レスポンスサイズを1回のマッチで取得できます。

名前付きキャプチャグループ(ES2018以降で利用可能)はログ解析の可読性を大幅に向上させます。match[1]、match[2]の代わりにmatch.groups.ip、match.groups.timestampを使えます。構文は(?<name>pattern)です。キャプチャグループが2つ以上ある場合は常にこれを使いましょう。将来の自分が感謝するはずです。

複数行にまたがるログエントリ(Javaのスタックトレースなど)には、/sフラグ(dotAll)を使って.が改行にもマッチするようにするか、クロスエンジン互換の[\s\S]を使います。怠惰量指定子と組み合わせます:/ERROR[\s\S]*?(?=\n\d{4}-|$)/はERRORから次のログエントリのタイムスタンプまでマッチします。

性能の警告:バックトラッキングは特定の入力で正規表現を指数的に遅くします。パターン/(a+)+b/が文字列"aaaaaaaaaaaaaaaaac"に対して秒単位の時間がかかるのは、エンジンが内側と外側のグループ間でaの分割を全通り試すためです。信頼できない入力(ユーザー投稿テキスト、未知ソースのログ)を解析する場合は、RE2互換パターンを使うかタイムアウトを設定してください。

// 名前付きグループによるApacheアクセスログ解析
const logPattern = /^(?<ip>\S+) \S+ \S+ \[(?<time>[^\]]+)\] "(?<method>\S+) (?<path>\S+) \S+" (?<status>\d+) (?<size>\d+)/;

const line = '192.168.1.1 - - [21/May/2026:10:15:32 +0000] "GET /api/users HTTP/1.1" 200 1234';
const { groups } = line.match(logPattern);
// groups.ip = "192.168.1.1"
// groups.status = "200"
// groups.path = "/api/users"

// ログ行からすべてのkey=valueペアを抽出
const kvPattern = /(?<key>[\w.]+)=(?<value>"[^"]*"|\S+)/g;
const entries = [...line.matchAll(kvPattern)].map(m => m.groups);

正規表現が不適切な場面

HTMLの解析:やめてください。HTMLを正規表現で解析することについてのStack Overflowの有名な回答(Zalgoを召喚する話)が面白いのは、それが正しいからです。HTMLは正規言語ではありません。ネストされたタグ、自己閉じ要素、引用符内に引用符を含む属性、CDATAセクション、コメント — これらすべてが正規表現を破壊します。ブラウザではDOMParser、Node.jsではcheerio/jsdomを使ってください。

JSONの検証:正規表現ではネストされたJSONを検証できません。対応する波括弧を数えられないためです。何かがJSONのように見えるか(/^\s*[{\[]/)は確認できますが、有効かどうかは検証できません。try/catch内でJSON.parse()を使ってください。

算術式:括弧のネストや演算子の優先順位がある場合はパーサーが必要で、正規表現では対応できません。計算機や数式評価器を構築するなら、再帰下降パーサーやパーサーコンビネータ(nearley.jsやPEG.jsなど)を検討してください。

自然言語処理:正規表現はテキストのトークン化や単純なパターン検出はできますが、文法の理解、曖昧性の処理、文脈の考慮はできません。文分割は「Dr. Smith went to Washington, D.C. He arrived at 3 p.m.」のように、すべてのピリオドが曖昧な場合に難しくなります。単純なパターンマッチング以上のことにはNLPライブラリ(spaCy、compromise)を使いましょう。

正規表現の性能チューニングと注意点

コンパイルは一度、使用は複数回。JavaScriptではループの外で正規表現を定義してください:const pattern = /\d+/g;(内側ではなく)。Pythonではre.compile()を使います。コンパイルコストは小さいですが、数百万回の反復で積み重なります。ベンチマークでは、プリコンパイルされた正規表現は1000万回のマッチでインライン正規表現の3倍高速でした。

破滅的なバックトラッキングを避けてください。/(.+)+@/や/(a|a)+b/のようなパターンは、マッチしない入力で指数的な時間がかかります。ルール:重複するパターンの量指定子をネストしないこと。確信がない場合は、病理的な入力(ほぼマッチする繰り返し文字の長い文字列)でテストしてください。regex101.comのデバッガがバックトラッキングのステップを表示してくれます。

利用可能な場合はアトミックグループや所有量指定子を使います。Javaと.NETでは(?>pattern)がグループへのバックトラッキングを防ぎます。JavaScriptはまだサポートしていません(2026年時点でStage 3のプロポーザル)が、パターンを再構成することで回避できます。(.*)\dを([^\d]*)\dに置き換える — 文字クラスが数字にマッチできないため、バックトラッキングの余地がなくなります。

大規模テキスト処理(数GBのログスキャン)には、バックトラッキングエンジンの代わりに有限オートマトンベースのツールを検討してください。ripgrepはRustのregexクレート(RE2のアプローチに基づく)を使用し、2〜5 GB/sでテキストを処理します。複雑なパターンではgrep -Pの100倍高速です。バックトラッキングしないためです。