REGEX CHEATSHEET

Python 正規表現(re)チートシート

「数字だけ抜きたい」「日付を分けたい」から引ける逆引き表です。コードはすべて Python 3.12.10 で実行し、出力をそのまま載せています。

🎯 対象: 正規表現を毎回検索している初心者〜中級者 ⏱️ 読了: 約11分 🐍 検証: Python 3.12.10 / Windows 11

結論:探すなら re.search、全部抜くなら re.findall、置換は re.sub、入力の形チェックは re.fullmatch。パターンは r"..." で書く。

1. 逆引き早見表

「結果」は Python 3.12.10 で実行した出力です。

やりたいこと書くコード結果
数字を全部抜くre.findall(r"\d+", "注文番号A-1234、数量 5、単価 980円")['1234', '5', '980']
最初の数字だけre.search(r"\d+", "注文番号A-1234、数量 5").group()1234
「円」の前の数字だけ(先読み)re.findall(r"\d+(?=円)", "単価 980円、数量 5個、送料 500円")['980', '500']
メール形式の簡易チェックbool(re.fullmatch(r"[\w.+-]+@[\w-]+(\.[\w-]+)+", s))taro@example.com → True
taro@example → False
日付を拾うre.findall(r"\d{4}[-/]\d{1,2}[-/]\d{1,2}", text)['2026-09-29', '2026/10/15']
日付を年月日に分けるre.search(r"(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})", "受付 2026-09-29 完了").groupdict(){'y': '2026', 'm': '09', 'd': '29'}
並べ替えて置換re.sub(r"(\d{4})-(\d{2})-(\d{2})", r"\3/\2/\1", "2026-09-29")29/09/2026
空白をまとめるre.sub(r"\s+", " ", "a b \t c\n d")a b c d
複数の区切りで分割re.split(r"[,;\s]+", "a, b;c d")['a', 'b', 'c', 'd']
タグを1つずつ(非貪欲)re.findall(r"<.+?>", html)['<b>', '</b>', '<i>', '</i>']
かぎかっこの中だけre.findall(r"「(.+?)」", "「はい」と「いいえ」で答える")['はい', 'いいえ']
URL を拾うre.findall(r"https?://[^\s<>\"']+", page)['https://pythonland.tech/', 'http://example.com/a?b=1']
電話番号・郵便番号r"0\d{1,4}-\d{1,4}-\d{4}"/r"\d{3}-\d{4}"['03-1234-5678', '090-1234-5678']/下の注意

import は import re の1行だけです。入力は、日付の text が "受付 2026-09-29、納期 2026/10/15、参考 2026年10月20日"、page が "see https://pythonland.tech/ and http://example.com/a?b=1 now" です。電話番号は "TEL 03-1234-5678 / 090-1234-5678" に当て、html は 3章の例を使いました。拾った日付の計算は日付・時刻チートシートへ。

⚠️
メールと郵便番号は「形の簡易チェック」

メールのパターンは打ち間違いを弾く程度で、規格(RFC 5322)の書き方すべてには対応していません。太郎@example.com や a..b@example.com も通りました。実在は確認メールなど別の手段で確かめます。

郵便番号の \d{3}-\d{4} は "〒100-0001 東京都 03-1234-5678" から ['100-0001', '234-5678'] を返し、電話番号の一部まで拾いました。入力値の判定は re.fullmatch で全体を見ます($ の落とし穴は失敗④)。

2. re の関数7つの使い分け

関数返すもの使いどころ
matchMatch か None(先頭で一致時)行頭の形を見る
searchMatch か None(最初の1か所)含まれているか・最初の1つ
fullmatchMatch か None(全体が一致時)入力値の形式チェック
findalllist(文字列かタプル)全部抜き出す
finditerMatch のイテレータ位置も知りたい
sub置換後の str(件数つきは subn)置換・整形
splitlist複数種類の区切りで分割

r"\d+" が一致したか(bool())の結果です。

関数"123""123abc""abc123"
matchTrueTrueFalse
searchTrueTrueTrue
fullmatchTrueFalseFalse

findall はグループの数で戻り値が変わる

text = "税込 1,200円と 3,400円"
print(re.findall(r"\d+,\d+円", text))     # → ['1,200円', '3,400円']  グループなし
print(re.findall(r"(\d+,\d+)円", text))   # → ['1,200', '3,400']  1個=そのグループ
print(re.findall(r"(\d+),(\d+)円", text)) # → [('1', '200'), ('3', '400')]  2個以上=タプル
print(re.findall(r"(?:\d+,\d+)円", text)) # → ['1,200円', '3,400円']  (?:...) は数えない

まとめたいだけのかっこは非キャプチャの (?:...) にします。位置が要るなら finditer で、"10個、20個、300個" から m.span() が (0, 2)・(4, 6)・(8, 11) と取れました。

sub は関数も渡せる・split は区切りも残せる

print(re.sub(r"(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})", r"\g<y>年\g<m>月\g<d>日", "2026-09-29"))  # → 2026年09月29日
print(re.sub(r"\d+", lambda m: str(int(m.group()) * 2), "3個と10個"))  # → 6個と20個
print(re.subn(r"\d+", "#", "3個と10個"))         # → ('#個と#個', 2)
print(re.sub(r"\d+", "#", "1 2 3 4", count=2))  # → # # 3 4

置換先には \1・\g<name> のほか関数も渡せます。split はキャプチャがあると区切りも残り、re.split(r"(,)", "a,b,c") は ['a', ',', 'b', ',', 'c'] でした。

count・maxsplit・flags はキーワード引数で書きます。sub・subn・split で位置引数として渡す書き方は 3.13 で非推奨になりました。

str のメソッドで済むなら使わない

固定の文字の置換や1種類の区切りなら "2026-09-29".replace("-", "/") や "a,b,c".split(",") が読みやすく安全です(基本文法の「文字列操作」)。ただし "123".isdigit() は全角でも True なので、半角数字の判定は re.fullmatch(r"[0-9]+", s) です。

3. メタ文字・貪欲と非貪欲・raw 文字列・フラグ

記号意味記号意味
.改行以外の1文字\d数字(既定は全角も)
^ $先頭・末尾($ は末尾の改行の手前にも一致)\w英数字と _(既定は日本語も)
* + ?0回以上・1回以上・0か1回\s空白・改行(既定は全角スペースも)
{m,n}m〜n 回({m} はちょうど m 回)\b単語の境目
[abc] [^abc]どれか1文字・それ以外の1文字|または
(...)グループ\記号を文字として扱う
(?=...) (?<=...)後ろ/前が … のときだけ(その部分は含めない)\A \Z文字列の先頭・末尾(改行を特別扱いしない)

貪欲と非貪欲(? で最短)

html = "<b>太字</b>と<i>斜体</i>"
print(re.findall(r"<.+>", html))    # → ['<b>太字</b>と<i>斜体</i>']
print(re.findall(r"<.+?>", html))   # → ['<b>', '</b>', '<i>', '</i>']
print(re.findall(r"<[^>]+>", html)) # → ['<b>', '</b>', '<i>', '</i>']

* + はできるだけ長く一致します(貪欲)。? を足すと最短に。4行目のように「止めたい文字以外」を [^...] で書くと意図がはっきりします。

raw 文字列(r"...")を付ける理由

x = "\d+"  # SyntaxWarning: invalid escape sequence '\d'
print(repr("\b"), repr(r"\b"))       # → '\x08' '\\b'
print(re.findall("\bcat\b", "cat"))  # → []
print(re.findall(r"\bcat\b", "cat")) # → ['cat']

Python の文字列も \ を特別扱いするためです。"\b" はバックスペースになり、警告も出ずに一致しなくなります。"\d" の警告は 3.12 で SyntaxWarning に格上げされ、将来は SyntaxError になる予定です。

フラグ

フラグ効果
re.IGNORECASE(re.I)大文字小文字を無視
re.MULTILINE(re.M)^ $ が各行に効く。3行の文字列で ^\w+ が ['first'] → ['first', 'second', 'third']
re.DOTALL(re.S). が改行にも一致
re.VERBOSE(re.X)空白を無視し # でコメントを書ける
re.ASCII(re.A)\d \w \s \b を半角だけに

複数は | でつなぎ、re.findall(r"^python", "Python\npython", re.IGNORECASE | re.MULTILINE) は ['Python', 'python'] でした。

pat = re.compile(r"""
    (?P<user>[\w.+-]+)   # ユーザー名
    @
    (?P<domain>[\w-]+(\.[\w-]+)+)  # ドメイン
""", re.VERBOSE)
m = pat.fullmatch("taro@example.co.jp")
print(m.group("user"), m.group("domain"))  # → taro example.co.jp

4. グループと re.compile を使うべきとき

m = re.search(r"(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})", "受付 2026-09-29 完了")
print(m.group("y"), m.group("m"), m.group("d"))  # → 2026 09 29
print(m.groupdict())  # → {'y': '2026', 'm': '09', 'd': '29'}
print(m.groups())     # → ('2026', '09', '29')
print(m.span())       # → (3, 13)

(?P<名前>...) で名前を付けると番号を数えずに取り出せます。一致しなかったグループは None で、re.match(r"(\d+)(円)?", "100").groups() は ('100', None) でした。

後方参照 \1 は「1番目のグループと同じ文字列」です。re.findall(r"(\w)\1", "aabbcd") は ['a', 'b'] でした。

re.compile は何度も使うときだけでよい

「re.compile() やモジュールレベルのマッチング関数に渡された最新のパターンはコンパイル済みのものがキャッシュされるので、一度に正規表現を少ししか使わないプログラムでは正規表現をコンパイルする必要はありません。」(re — 正規表現操作、© Python Software Foundation)

同じパターンの search 10万回は re.search: 0.054s / compiled: 0.030s で、差は約0.024秒。数回呼ぶ程度なら、速度は compile の理由になりにくい差です(1回の計測)。選ぶのは次のときです。

  • 同じパターンをループで何万回も使う
  • パターンに名前を付けて見通しを良くしたい(EMAIL.fullmatch(s) のように読める)

5. よくある失敗6つ

① match が先頭しか見ていない

print(re.match(r"\d+", "abc123"))   # → None
print(re.search(r"\d+", "abc123"))  # → <re.Match object; span=(3, 6), match='123'>

公式ドキュメントも、MULTILINE を付けても match は文字列の先頭だけを見ると注意しています。3.15 で加わった別名 re.prefixmatch() は FAQ へ。

② 記号のエスケープ忘れ

print(re.findall(r"a.c", "abc a.c a-c"))   # → ['abc', 'a.c', 'a-c']
print(re.findall(r"a\.c", "abc a.c a-c"))  # → ['a.c']
print(re.escape("price (税込) 1.5+"))       # → price\ \(税込\)\ 1\.5\+

. + ( を文字として探すなら \ を付けます。ユーザーの入力を検索語にするときは re.escape() で記号をまとめて無効化します。

③ 日本語と \d・\w・\s

print(re.findall(r"\d+", "全角123と半角456"))            # → ['123', '456']
print(re.findall(r"\d+", "全角123と半角456", re.ASCII))  # → ['456']
print(re.findall(r"\w+", "Python 3.12 と 正規表現、テスト_01"))  # → ['Python', '3', '12', 'と', '正規表現', 'テスト_01']
print(re.split(r"\s+", "a b c"))  # → ['a', 'b', 'c'](全角スペースでも分割)
print(re.findall(r"\bPython\b", "Python入門 pythonland Python3"))  # → []

\d \w \s は既定で Unicode 全体が相手で、\w はひらがな・漢字・カタカナ(長音「ー」も)に一致しました。半角だけなら re.ASCII か [0-9] で書きます。全角数字は int() にも通る(int(re.search(r"\d+", "全角123").group()) は 123)ので、エラーにならずに混ざります。

最後の行が空なのは、「入」も「3」も \w なので境目にならないためです。かなは [ぁ-ん]+・[ァ-ヶー]+ で拾えます。

④ $ が末尾の改行の手前に一致する

print(re.search(r"^\d+$", "123\n"))  # → <re.Match object; span=(0, 3), match='123'>
print(re.fullmatch(r"\d+", "123\n"))  # → None

ファイルから読んだ行は末尾に改行が付いていることが多いので、入力チェックは fullmatch(または末尾を \Z)にします。

⑤ None に .group() を呼ぶ

re.match(r"\d+", "abc").group() は AttributeError: 'NoneType' object has no attribute 'group' になりました。if m: で一致を確かめてから呼びます(読み方はエラー辞典の AttributeError)。

⑥ パターンの書き間違いで re.error

try:
    re.compile("(")
except re.error as e:
    print(e)  # → missing ), unterminated subpattern at position 0

r"+abc" なら nothing to repeat at position 0 です。入力されたパターンを使うアプリでは except re.error で受けて表示します。3.13 で re.PatternError に改名されましたが、re.error の名前も残っています(3.12.10 には PatternError がありません)。

6. 正規表現を使っているアプリで見る

アプリ使っている関数・機能何のため
ログファイルアナライザー名前付きグループ入りのパターン定義。改造課題で re.match+groupdict()ログ形式ごとのパターンの定義例(本体の色分けは文字列の包含判定)
ファイル一括リネーマーre.sub+except re.error入力したパターンでファイル名を置換。不正なパターンはエラー表示
シンタックスハイライト付きエディタfinditer+re.DOTALL、非貪欲 <!--.*?-->一致の位置を色付けの範囲に変換
Markdownエディターre.MULTILINE、キャプチャ付き split、\1 の sub見出しの判定と **太字** の置換
正規表現テスターcompile+finditer・subn・split1回コンパイルし、選んだモードで使う

7. 自分のパターンを試す

正規表現テスターで一致箇所を目で確かめてからコードに移すと失敗が減ります。

  • モードは4つ:ハイライト/置換(件数つき)/分割/グループ抽出
  • フラグはチェックボックス:i・m・s・u(u は既定と同じ動きで、付けても変わらない)
  • サンプル14パターン(メール・URL・日付・郵便番号など)

パターン欄の文字列はそのまま re.compile に渡るので、\ は二重にしません(r"..." の中身を打つ)。

次の2つはサンプルのパターンを Python 3.12.10 のコードで実行した結果です。メールは半角限定で 太郎@example.com は通りません。コメント行の ^\s*#.*$ を "a = 1\n\n# comment\nb = 2" に当てると、m なしで []、ありで ['\n# comment'] となり、直前の空行の改行まで含みました。

8. よくある質問(FAQ)

Q. メールアドレスを正規表現で正確に検証できますか?

規格の書き方すべてを1本で扱うのは現実的ではありません。この記事のパターンは簡易チェックで、日本語や連続ドットも通ります。実在は確認メールなど別の手段で確かめます。

Q. 正規表現と str のメソッドはどちらを使えばいいですか?

固定の文字の置換・1種類の区切りなら str.replace・str.split です。「数字が続く」「区切りが複数種類」など形で指定したいときに正規表現を使います。

Q. Python 3.15 で re.match は使えなくなりますか?

使えます。3.15(2026-10-09 リリース)で同じ動作の別名 re.prefixmatch() が加わり、match は soft deprecated(新規コードでは別名を勧める扱い・警告なし・削除予定なし)になりました。3.14 以前でも動かすコードは match のままで構いません(正式版の What's New と re のドキュメントで確認)。詳しくは3.15 の変更点へ。

本記事は、生成AIを活用して下書きし、運営者が内容を確認・編集したうえで公開しています。AIの利用方針は免責事項をご覧ください。

9. まとめと次の一歩

  • 探す=search、全部=findall、置換=sub、形の判定=fullmatch。match は先頭だけ
  • パターンは r"..."。"\b" は警告なしで壊れる
  • * + は貪欲。最短なら ? を足すか [^...] で止める
  • \d \w \s は全角・日本語にも一致。半角だけなら re.ASCII か [0-9]

まずは正規表現テスターで早見表の行を自分のデータに当ててください。re の位置づけはライブラリ一覧、作るものは初心者向け・中級者向けのアプリ一覧へ。

📌
参考にした一次情報と検証環境(2026-09-29 確認)

仕様は公式ドキュメント日本語版の re — 正規表現操作/正規表現 HOWTO、版ごとの変更は What's New 3.12/3.13/3.14、3.15 は What's New 3.15(正式版・2026-10-12 確認)を参照。出力はすべて Python 3.12.10(Windows 11)で実行した結果です。