正規表現リファレンス - パターンマッチングの基本から実践まで

正規表現(Regular Expression / regex)は、文字列のパターンマッチングに使われる表記法。検索・置換・バリデーションなど幅広い場面で利用される。

基本的なメタ文字

メタ文字意味マッチする文字列
.任意の1文字(改行以外)a.cabc, aXc, a1c
\d数字 [0-9]\d{3}123, 456
\D数字以外\D+abc, XYZ
\w英数字とアンダースコア [a-zA-Z0-9_]\w+hello_123
\W\w以外\W@, #,
\s空白文字(スペース、タブ、改行等)\s+ , \t\n
\S空白以外\S+hello
\\バックスラッシュ自体をエスケープ\\n\n(リテラル)

量指定子(Quantifiers)

パターンの繰り返し回数を指定する。

量指定子意味マッチする文字列
*0回以上ab*cac, abc, abbc
+1回以上ab+cabc, abbcacは不可)
?0回または1回colou?rcolor, colour
{n}ちょうどn回\d{4}2026
{n,}n回以上\d{2,}12, 123, 1234
{n,m}n回以上m回以下\d{2,4}12, 123, 1234

貪欲マッチと最短マッチ

デフォルトの量指定子は**貪欲(greedy)で、できるだけ長くマッチする。?を付けると最短(lazy)**マッチになる。

対象文字列: <div>hello</div><div>world</div>

貪欲:  <div>.*</div>   → <div>hello</div><div>world</div>
最短:  <div>.*?</div>  → <div>hello</div>
貪欲最短意味
**?0回以上(最短)
++?1回以上(最短)
???0回または1回(最短)
{n,m}{n,m}?n〜m回(最短)

アンカー(位置指定)

文字ではなく「位置」にマッチする。

アンカー意味マッチ
^行頭^Hello行頭のHello
$行末world$行末のworld
\b単語境界\bcat\bcatcatchは不可)
\B単語境界以外\Bcatcatchcat部分

^$はデフォルトでは文字列全体の先頭・末尾にマッチする。複数行モード(mフラグ)を有効にすると各行の先頭・末尾にマッチする。

文字クラス

[]で囲んで、マッチさせたい文字の集合を定義する。

[abc]       # a, b, c のいずれか
[a-z]       # 小文字アルファベット
[A-Z]       # 大文字アルファベット
[0-9]       # 数字(\d と同等)
[a-zA-Z0-9] # 英数字
[^abc]      # a, b, c 以外(否定)
[^0-9]      # 数字以外(\D と同等)

ハイフン-を文字としてマッチさせたい場合は、先頭か末尾に置くかエスケープする。

[-abc]      # -, a, b, c のいずれか
[abc-]      # a, b, c, - のいずれか
[a\-c]      # a, -, c のいずれか

グループとキャプチャ

キャプチャグループ

()で囲むとグループ化され、マッチした部分を後から参照できる。

(\d{4})-(\d{2})-(\d{2})

2026-03-05に対して:

  • グループ1: 2026
  • グループ2: 03
  • グループ3: 05

名前付きキャプチャグループ

(?<name>...) で名前を付けられる。

(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})

非キャプチャグループ

(?:...)はグループ化のみ行い、キャプチャしない。パフォーマンスが若干良い。

(?:https?|ftp)://[\w./]+

後方参照

キャプチャした内容を同じパターン内で再利用する。\1は最初のキャプチャグループを参照する。

(\w+)\s+\1    # 同じ単語の繰り返し("the the" 等)

選択(OR)

|でいずれかのパターンにマッチさせる。

cat|dog         # "cat" または "dog"
(cat|dog) food  # "cat food" または "dog food"

先読みと後読み(Lookaround)

マッチ位置の前後を条件として指定する。マッチ結果には含まれない(ゼロ幅アサーション)。

構文名称意味
(?=...)肯定先読み後ろに...が続く位置
(?!...)否定先読み後ろに...が続かない位置
(?<=...)肯定後読み前に...がある位置
(?<!...)否定後読み前に...がない位置
# 「円」が後に続く数字列
\d+(?=)
# → "1000円" の "1000" にマッチ("円"は含まない)

# 「$」が前にない数字列
(?<!\$)\d+
# → "$100 200" の "200" にマッチ

# パスワードバリデーション(8文字以上、英字と数字を両方含む)
^(?=.*[a-zA-Z])(?=.*\d).{8,}$

フラグ(修飾子)

正規表現の動作を変更するオプション。言語によって指定方法が異なる。

フラグ名称効果
i大文字小文字無視Hellohello,HELLO等にもマッチ
gグローバル最初の1つだけでなく全てのマッチを返す
m複数行モード^$が各行の先頭・末尾にマッチ
sドットオール.が改行にもマッチ
uUnicodeUnicode対応(\p{}が使える等)

JavaScript での指定

const regex = /hello/gi
// または
const regex = new RegExp('hello', 'gi')

Python での指定

import re
re.findall(r'hello', text, re.IGNORECASE | re.MULTILINE)

実践パターン集

メールアドレス(簡易)

[\w.+-]+@[\w-]+\.[\w.]+

RFC準拠の完全なバリデーションは非常に複雑なため、実務では簡易パターン+ライブラリの併用が推奨される。

URL

https?://[\w!?/+\-_~;.,*&@#$%()'[\]]+

日本の電話番号

0\d{1,4}-\d{1,4}-\d{4}

郵便番号

\d{3}-\d{4}

日付(yyyy-mm-dd)

\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])

IPv4アドレス

(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)

HTMLタグの除去(置換用)

<[^>]+>

HTMLの解析には正規表現ではなくDOMパーサーを使うべきだが、簡易的なタグ除去には使える。

前後の空白を除去(トリム)

^\s+|\s+$

CSVの値を分割(ダブルクォート考慮)

"([^"]*(?:""[^"]*)*)"|([^,]+)

言語別の主なAPI

JavaScript

// テスト(マッチするか)
/\d+/.test('abc123')           // true

// マッチ結果の取得
'abc123'.match(/\d+/)          // ['123']
'abc123def456'.match(/\d+/g)   // ['123', '456']

// 置換
'hello world'.replace(/world/, 'regex')  // 'hello regex'

// 分割
'a,b,,c'.split(/,/)            // ['a', 'b', '', 'c']

// 名前付きグループ
const m = '2026-03-05'.match(/(?<y>\d{4})-(?<m>\d{2})-(?<d>\d{2})/)
m.groups  // { y: '2026', m: '03', d: '05' }

Python

import re

# テスト
re.search(r'\d+', 'abc123')       # <re.Match object>

# 全マッチ
re.findall(r'\d+', 'abc123def456')  # ['123', '456']

# 置換
re.sub(r'world', 'regex', 'hello world')  # 'hello regex'

# 分割
re.split(r',', 'a,b,,c')          # ['a', 'b', '', 'c']

# 名前付きグループ
m = re.match(r'(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})', '2026-03-05')
m.group('y')  # '2026'

Pythonでは名前付きグループの構文が(?P<name>...)である点に注意(Pが必要)。

Java

import java.util.regex.*;

Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher("abc123def456");

// テスト
m.find()          // true

// 全マッチ
while (m.find()) {
    System.out.println(m.group());  // "123", "456"
}

// 置換
"hello world".replaceAll("world", "regex");  // "hello regex"

Javaではバックスラッシュを\\でエスケープする必要がある。

よくある落とし穴

貪欲マッチによる過剰マッチ

.*は改行以外の全てを最大限マッチするため、意図より広い範囲をマッチしやすい。最短マッチ.*?や否定文字クラス[^...]+を検討する。

エスケープ忘れ

.*+?()[]{}|^$\はメタ文字。リテラルとして使うにはエスケープが必要。

# IPアドレスの「.」はエスケープが必要
192\.168\.1\.1    # ✅ 正しい
192.168.1.1       # ❌ 任意の文字にマッチしてしまう

^$の挙動

デフォルトでは文字列全体の先頭・末尾にマッチする。複数行テキストで各行を対象にするにはmフラグが必要。

バックトラッキングによる性能問題

ネストした量指定子(例: (a+)+)は指数関数的なバックトラッキングを引き起こし、処理が極端に遅くなる場合がある(ReDoS)。ユーザー入力に対して正規表現を使う場合は特に注意が必要。

# 危険なパターン(ReDoS)
(a+)+$

# 安全な書き換え
a+$