【Python】reモジュールで正規表現を使う

Python

こんにちは、かつコーチです。

「メールアドレスの形式をチェックしたい」「文字列から特定のパターンだけ抜き出したい」。
こうした処理をinsplitだけで書こうとすると、コードが複雑になりがちです。

そこで役立つのが正規表現です。
正規表現とは、文字列のパターンを記号を使って表現する仕組みで、Pythonではreモジュールを使って扱います。

reモジュールは標準ライブラリなので、追加インストールなしでimport reとするだけで使えます。
この記事では、reモジュールの主要な関数の違いと、実務でよく使うパターンを解説します。

reモジュールの基本操作

match・search・findallの違い

reモジュールには、文字列を検索する関数がいくつかあります。
それぞれ挙動が異なるため、最初に違いを整理しておきましょう。

import re

text = "電話番号は090-1234-5678です"
pattern = r"\d{3}-\d{4}-\d{4}"

# match: 文字列の先頭からのみマッチを試みる
result_match = re.match(pattern, text)
print(result_match)  # None(先頭が数字ではないため)

# search: 文字列全体から最初の1件だけを探す
result_search = re.search(pattern, text)
print(result_search.group())  # 090-1234-5678

# findall: 文字列全体からすべてのマッチをリストで取得する
result_findall = re.findall(pattern, text)
print(result_findall)  # ['090-1234-5678']

re.match()は文字列の先頭からしかマッチを判定しません。
そのため、途中にパターンがあってもNoneが返る点に注意が必要です。

re.search()は文字列のどこにあってもマッチを1件見つけてくれます。
re.findall()は該当するすべての箇所をリストとして返すため、複数件抽出したいときに使います。

基本的なパターン記法

正規表現でよく使う記法を、実例とあわせて紹介します。

import re

# \d: 数字1文字
print(re.findall(r"\d", "abc123"))       # ['1', '2', '3']

# \d+: 数字が1文字以上連続
print(re.findall(r"\d+", "abc123def456"))  # ['123', '456']

# [a-zA-Z]+: 英字の連続
print(re.findall(r"[a-zA-Z]+", "abc123def"))  # ['abc', 'def']

# .*: 任意の文字が0文字以上
print(re.findall(r"a.*z", "a123z"))  # ['a123z']

# ^と$: 文字列の先頭・末尾
print(re.match(r"^abc", "abcdef"))   # マッチする

\dは数字、+は「直前の文字が1回以上繰り返す」という意味です。
[a-zA-Z]のような角かっこは「この範囲のいずれか1文字」を表します。

文字列を置換する(re.sub)

パターンにマッチした部分を別の文字列に置き換えるにはre.sub()を使います。

import re

text = "電話番号は090-1234-5678です"
result = re.sub(r"\d{3}-\d{4}-\d{4}", "[非公開]", text)

print(result)
# 電話番号は[非公開]です

第1引数にパターン、第2引数に置換後の文字列、第3引数に対象の文字列を渡します。
個人情報のマスキングや、不要な空白文字の削除など、実務でよく使う処理です。

グループ化で部分を取り出す

かっこ()で囲むと、マッチした文字列の一部分だけを取り出せます。
これをグループ化と呼びます。

import re

text = "2026-08-22"
pattern = r"(\d{4})-(\d{2})-(\d{2})"

match = re.search(pattern, text)
if match:
    year, month, day = match.groups()
    print(f"{year}年{month}月{day}日")
    # 2026年08月22日

match.groups()を使うと、各グループのマッチ結果をタプルで取得できます。
日付や住所など、構造を持った文字列を分解したいときに便利です。

よくあるつまずきポイント・エラー対処

matchとsearchを混同してマッチしない

Before(つまずいたコード)

import re

log_line = "[ERROR] 2026-08-22 処理に失敗しました"
pattern = r"\[ERROR\]"

result = re.match(pattern, log_line)
print(result)

このコード自体は動きますが、私は別のログ行で似たコードを書いていてハマったことがあります。

log_line = "ログ出力: [ERROR] 処理に失敗しました"
result = re.match(pattern, log_line)
print(result)
# None

行の先頭に[ERROR]がない場合、re.match()Noneを返してしまいます。
「マッチしているはずなのに条件分岐に入らない」というバグの原因になり、原因究明に時間がかかりました。

After(改善したコード)

import re

log_line = "ログ出力: [ERROR] 処理に失敗しました"
pattern = r"\[ERROR\]"

result = re.search(pattern, log_line)
if result:
    print("エラー行を検出しました")

文字列の途中にパターンがある可能性がある場合は、re.match()ではなくre.search()を使いましょう。
「先頭一致で十分か、文中のどこかにあればよいか」を意識して使い分けることが大切です。

エスケープ忘れで意図しない記号にマッチする

Before(つまずいたコード)

import re

text = "価格は1000円です"
pattern = r"\d+.円"  # ドットをそのままの意味で使ったつもり

result = re.search(pattern, text)
print(result.group() if result else "マッチなし")

正規表現では.は「任意の1文字」を意味する特殊記号です。
「.(ピリオド)そのもの」を検索したいつもりでも、意図せず別の1文字にもマッチしてしまいます。

マッチした文字列: 1000円

一見正しく見えますが、.が「円の前の任意の1文字」にマッチしているだけで、本来意図した「ピリオド」の検索にはなっていません。

After(改善したコード)

import re

text = "価格は1000.5円です"
pattern = r"\d+\.\d+円"  # ドットをエスケープして「.そのもの」として扱う

result = re.search(pattern, text)
print(result.group() if result else "マッチなし")
# 1000.5円

.(など、正規表現で特別な意味を持つ記号をそのまま検索したい場合は、バックスラッシュ\でエスケープします。
特殊記号かどうか迷ったら、Pythonの公式ドキュメントで意味を確認する習慣をつけましょう。

まとめ

この記事のポイント

  • re.match()は先頭一致、re.search()は文中一致、re.findall()は全件取得です
  • \d+[]などの基本記法を組み合わせてパターンを作ります
  • re.sub()でマッチした部分を置換できます
  • ()によるグループ化で、マッチの一部だけを取り出せます
  • .のような特殊記号を文字通り検索したい場合は\でエスケープします

次に読むべき記事

ファイルパスの操作をより現代的に書きたい方は、pathlibの解説記事もあわせてご覧ください。
CSVファイルの読み書きに正規表現を組み合わせたい方は、csvモジュールの記事も参考になります。

タグ: Python, 中級者向け, 標準ライブラリ

タイトルとURLをコピーしました