Pythonで高度なテキスト処理を行う際に不可欠な re モジュールについて、主要な関数、正規表現の特殊文字(メタ文字)、およびRaw文字列の挙動を体系的に解説する。
複雑になりがちな正規表現の記法も、一覧として整理しておくことで実装時のリファレンスとして活用できる。
目次
1. Raw文字列(Raw String)の使用
正規表現ではバックスラッシュ(\)をエスケープ文字として多用する。しかし、Pythonの通常の文字列でもバックスラッシュはエスケープとして扱われるため、正規表現エンジンにバックスラッシュを渡すには \\ と記述する必要があり、記述が煩雑になる。
文字列の先頭に r を付けた「Raw文字列」を使用することで、バックスラッシュを文字通りに解釈させることができ、可読性が劇的に向上する。
import re
# 通常の文字列:バックスラッシュを表現するために、さらにエスケープが必要(読みにくい)
# 意図:\my-host\.*
pattern_normal = "\\\\my-host\\\\.*"
# Raw文字列:見たまま記述できる(推奨)
pattern_raw = r"\\my-host\\.*"
2. reモジュールの主要関数
頻繁に使用する4つの関数と、その戻り値を以下の表にまとめた。
| 関数名 | 戻り値 | 動作の解説 |
re.search() | re.Match オブジェクト | 文字列全体を走査し、最初に見つかったマッチ箇所を返す。見つからない場合は None。 |
re.findall() | list (文字列のリスト) | 文字列内でパターンにマッチする部分をすべて探し、リストとして返す。 |
re.split() | list (文字列のリスト) | パターンにマッチした箇所で文字列を分割する。 |
re.sub() | str (置換後の文字列) | パターンにマッチした部分を、指定した文字列に置換する。 |
3. 代表的な正規表現(メタ文字)一覧
正規表現のパターンを構成する特殊文字の意味と使用例である。
| 正規表現 | 意味 | 例 | 例の解説 |
. | 改行以外の任意の1文字 | a.c | “abc”, “a1c” などにマッチ |
^ | 行の先頭 | ^Start | 行が “Start” で始まる場合にマッチ |
$ | 行の末尾 | End$ | 行が “End” で終わる場合にマッチ |
* | 直前の文字が0回以上 | ab*c | “ac”, “abc”, “abbc” などにマッチ |
+ | 直前の文字が1回以上 | ab+c | “abc”, “abbc” (acは不可) にマッチ |
? | 直前の文字が0回か1回 | ab?c | “ac”, “abc” のみにマッチ |
| ` | ` | いずれかのパターン(OR) | `Apple |
(...) | グループ化 | (ab)+ | “ab”, “abab” などの繰り返しにマッチ |
[...] | 文字セット(いずれか1文字) | [a-z] | 小文字アルファベット1文字にマッチ |
[^...] | 否定文字セット(以外) | [^0-9] | 数字以外の文字にマッチ |
\ | エスケープ | \. | 特殊文字 . を通常の文字として扱う |
{n} | 直前の文字がn回 | \d{3} | 数字が3つ続く(例: 123) |
{n,} | 直前の文字がn回以上 | \d{3,} | 数字が3つ以上続く |
{n,m} | 直前の文字がn回~m回 | \d{3,5} | 数字が3つ~5つの間 |
4. 実践的なコード例
ここまで解説した関数と正規表現記号を組み合わせ、非構造化テキストデータから必要な情報を抽出・整形するコード例を示す。ここでは、注文メールのテキストから商品IDや価格を抜き出す処理を想定する。
import re
def main():
# 解析対象のテキストデータ(注文メールの抜粋など)
text_data = """
Order Date: 2023-11-05
[Item] ID:PROD-001 Price:1200JPY Note:SALE
[Item] ID:PROD-888 Price:3500JPY Note:Standard
[Item] ID:ACC-99 Price:500JPY Note:Bulk
End of list.
"""
print("--- 1. re.findall で特定パターンを抽出 ---")
# 商品IDのパターン: アルファベット数文字 + ハイフン + 数字
# r"ID:([A-Z]+-\d+)" とすることで、ID部分のみをグループ抽出
id_list = re.findall(r"ID:([A-Z]+-\d+)", text_data)
print(f"抽出された商品ID: {id_list}")
print("\n--- 2. re.search で日付を検索 ---")
# 日付形式: 数字4桁-数字2桁-数字2桁
date_match = re.search(r"Date:\s*(\d{4}-\d{2}-\d{2})", text_data)
if date_match:
# group(1) で括弧内の日付部分を取得
print(f"注文日: {date_match.group(1)}")
print("\n--- 3. re.sub でデータを整形 ---")
# 価格の 'JPY' を削除し、カンマ区切りなどを想定して数値のみにする
# ここでは単純に 'JPY' を空文字に置換
clean_text = re.sub(r"Price:(\d+)JPY", r"Price: \1 Yen", text_data)
# 確認のため、Item行のみを表示
# splitlines() で行ごとに処理
print("整形後のデータ(抜粋):")
for line in clean_text.splitlines():
if "[Item]" in line:
print(line.strip())
print("\n--- 4. re.split で区切り文字による分割 ---")
# ID:PROD-001 などの行を、空白文字(スペースやタブ)で分割してリスト化
sample_line = "ID:PROD-001 Price:1200JPY"
# \s+ は1つ以上の空白文字にマッチする
parts = re.split(r"\s+", sample_line)
print(f"元データ: '{sample_line}'")
print(f"分割結果: {parts}")
if __name__ == "__main__":
main()
5. まとめ
Pythonの re モジュールは、単純な文字列検索では対応できない複雑なパターンマッチングを可能にする。
- Raw文字列
r"..."を使用してエスケープの煩雑さを避ける。 - 用途に応じて
search(単一検索) とfindall(一括抽出) を使い分ける。 - メタ文字の特性を理解し、柔軟な検索パターンを構築する。
これらを適切に組み合わせることで、ログ解析、スクレイピング、データクレンジングなどの効率を大幅に向上させることができる。
