フォームの入力チェックやテキストの検索・置換で「^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$」のような記号の並びを見たことがあるかもしれません。これは正規表現(Regular Expression)と呼ばれる、文字列のパターンを表すための記法です。この記事では正規表現の基本的な書き方と、メールアドレスや郵便番号などの実例を解説します。
1. 正規表現とは何か
正規表現とは、「どのような文字列にマッチするか」というパターンを、特定の記号(メタ文字)を組み合わせて表現する記法です。「数字が3桁続く」「英字とハイフンの組み合わせ」のような条件を、プログラムが処理できる形で書き表すことができます。
入力フォームのバリデーション、テキストエディタの検索・置換、ログファイルから特定のパターンを抜き出す処理など、文字列を扱う場面の多くで利用されています。実際の動作を確認したい場合は正規表現テスターでパターンと文字列を入力しながら試すと理解しやすくなります。
2. 基本のメタ文字一覧
正規表現でよく使われる記号(メタ文字)には、それぞれ決まった意味があります。代表的なものを整理すると次のようになります。
| 記号 | 意味 | 例 |
|---|---|---|
| . | 任意の1文字(改行を除く) | a.c は abc や axc にマッチ |
| * | 直前の文字が0回以上の繰り返し | ab* は a、ab、abb などにマッチ |
| + | 直前の文字が1回以上の繰り返し | ab+ は ab、abb にマッチ(aのみは不可) |
| ? | 直前の文字が0回または1回 | colou?r は color、colour にマッチ |
| ^ | 文字列の先頭 | ^abc は先頭がabcの文字列にマッチ |
| $ | 文字列の末尾 | abc$ は末尾がabcの文字列にマッチ |
| [ ] | 角括弧内のいずれか1文字 | [abc] は a、b、c のいずれかにマッチ |
| ( ) | グループ化・部分マッチの抽出 | (ab)+ は ab、abab などにマッチ |
| | | いずれかのパターン(OR) | cat|dog は cat または dog にマッチ |
| \d | 数字1文字(0-9) | \d{3} は3桁の数字にマッチ |
| \w | 英数字とアンダースコア1文字 | メールアドレスのローカル部などで使用 |
| \s | 空白文字(スペース・タブ・改行など) | 区切り文字の判定などで使用 |
{n}や{n,m}という記法を使うと「直前の文字をn回」「n回からm回」繰り返すという指定もできます。これらのメタ文字を組み合わせることで、複雑なパターンも表現できます。
3. よく使う実例:メールアドレス・郵便番号・電話番号・URL
実務でよく使われる簡易的なパターン例を紹介します。いずれも完全な仕様をすべて満たすものではなく、一般的な入力チェックの目的で使われる簡易版です。
| 用途 | 簡易パターン例 | 補足 |
|---|---|---|
| メールアドレス | ^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$ | ローカル部とドメイン部を@で区切る最低限の形を確認 |
| 郵便番号(日本) | ^\d{3}-\d{4}$ | 123-4567のようなハイフン区切りの7桁 |
| 電話番号(日本・簡易) | ^0\d{1,4}-\d{1,4}-\d{4}$ | 市外局番の桁数が地域で異なるため幅を持たせている |
| URL(簡易) | ^https?://[\w.-]+(/\S*)?$ | http/httpsで始まり、ドメインに続くパスを許容 |
これらはあくまで簡易パターンです。メールアドレスや電話番号の正式な仕様は例外的な形式も多く、完全に厳密な正規表現を書くと非常に複雑になります。実用上は「明らかにおかしい入力をはじく」程度の精度で運用し、最終的な確認はメール送信や本人確認など別の手段で行うのが一般的です。
4. フラグ(g・i・m・s)の意味
正規表現には、マッチの挙動を変える「フラグ」と呼ばれるオプションがあります。JavaScriptなどでは正規表現リテラルの末尾に付けて使います。
- •g(global):文字列内で最初の1件だけでなく、マッチする全ての箇所を対象にする
- •i(ignore case):大文字・小文字を区別せずにマッチさせる
- •m(multiline):複数行の文字列で^と$をそれぞれの行の先頭・末尾として扱う
- •s(dotall):通常改行にマッチしない . を、改行も含めて任意の1文字としてマッチさせる
例えば置換処理で文字列内の全ての一致箇所を変換したい場合はgフラグが必須で、これを付け忘れると最初の1件しか置換されない、という事象が起こりがちです。
5. 貪欲マッチと非貪欲マッチの違い
*や+などの繰り返しを表すメタ文字は、デフォルトでは「マッチできる限り長く」マッチしようとします。これを貪欲マッチ(greedy)と呼びます。一方、繰り返しの記号の後に?を付けると、「マッチできる限り短く」マッチする非貪欲マッチ(lazy)に変わります。
例えば文字列「<a><b>」に対して<.*>というパターンを使うと、貪欲マッチでは<a><b>全体にマッチしてしまいますが、<.*?>という非貪欲マッチに変えると<a>だけにマッチします。HTMLタグの抜き出しなど、区切りごとに短く一致させたい場面では非貪欲マッチを意識する必要があります。
6. 先読み・後読みで「条件は見るが含めない」指定をする
「ある文字列の直前・直後に特定のパターンがあるかを条件にしたいが、マッチした結果にはその部分を含めたくない」という場面では、先読み(lookahead)・後読み(lookbehind)という記法が使えます。(?=...)は直後に続くパターンを条件にする「肯定先読み」、(?!...)は直後に続いてはいけないパターンを指定する「否定先読み」です。同様に(?<=...)は直前のパターンを条件にする「肯定後読み」、(?<!...)は「否定後読み」になります。
例えば「3桁区切りのカンマを金額に自動で入れたい」という処理では、数字の並びのうち「末尾から3の倍数番目の位置」を後読みで判定してカンマを挿入する、といった使い方をします。少し高度な記法ですが、パスワードの強度チェック(英字・数字・記号がそれぞれ1文字以上含まれるか)のように、複数の条件を1つのパターンでまとめて表現したいときにも役立ちます。
7. パフォーマンスの落とし穴:破滅的バックトラッキング
(a+)+bのように、繰り返しの中にさらに繰り返しを入れ子にしたパターンを、マッチしない長い文字列に対して実行すると、処理時間が入力の長さに対して指数関数的に増え、ブラウザやサーバーが固まったように見えるほど遅くなることがあります。これは「破滅的バックトラッキング(catastrophic backtracking)」と呼ばれる現象で、正規表現エンジンがマッチの組み合わせを総当たりで試し続けてしまうために起こります。ユーザー入力をそのまま正規表現のパターンとして使うような設計は特に危険で、悪意のある入力によって意図的にサーバーを止められてしまう脆弱性(ReDoS)につながることもあるため、入れ子になった繰り返しを書くときは特に注意し、必要以上に複雑なパターンを避けるよう心がけましょう。
まとめ
正規表現は、メタ文字を組み合わせることで様々な文字列パターンを表現できる記法です。基本のメタ文字とフラグの意味を覚え、貪欲・非貪欲の違いを意識するだけでも、実用的な入力チェックや文字列処理の多くに対応できます。実際に手元のパターンを試したい場合は正規表現テスターを使って、マッチする範囲を確認しながら組み立ててみてください。