正規表現(Regular Expression)において、丸括弧 () を使用した「グループ化」は、マッチした文字列全体ではなく、その中の一部分だけを抜き出す際に非常に強力な機能です。
Pythonの標準ライブラリである re モジュールの findall 関数とグループ化を組み合わせることで、構造化されたテキストデータを効率的にリスト化する手法を解説します。
目次
解決したい課題
ログファイルや定型フォーマットのテキストデータから、特定の項目(ID、コード、名称など)だけを分離して抽出したい場合があります。単純なマッチングでは行全体が取得されてしまいますが、グループ機能を使うことで、必要な要素だけをタプルとして取得できます。
実装例:サーバーアクセスログの解析
ここでは、サーバーのアクセスログを模したテキストデータから、「リクエストID」「メソッド」「パス」の3つの要素を抽出するシナリオを想定します。
ソースコード
import re
# 解析対象のテキストデータ(サーバーログを想定)
# フォーマット: [RequestId] Method Path
log_data = """
[Req001] GET /index.html
[Req002] POST /api/login
[Req003] GET /css/style.css
[Req004] DELETE /api/users/10
[Req005] PUT /api/settings
"""
# 正規表現パターンの定義
# () で囲んだ部分がグループとして抽出されます
# 1. \[(Req\d+)\] : []の中の Req + 数字 を抽出
# 2. ([A-Z]+) : 大文字のアルファベット(メソッド)を抽出
# 3. (/[a-zA-Z0-9/._-]+) : パスを抽出
pattern = r'\[(Req\d+)\]\s+([A-Z]+)\s+(/[a-zA-Z0-9/._-]+)'
# re.findall でパターンにマッチする部分を全て検索
# グループ化を使用しているため、各マッチは (グループ1, グループ2, グループ3) のタプルになります
matches = re.findall(pattern, log_data)
# 結果の出力
print(f"抽出された件数: {len(matches)}")
print("-" * 30)
for request_id, method, path in matches:
print(f"ID: {request_id} | Method: {method:6} | Path: {path}")
実行結果
抽出された件数: 5
------------------------------
ID: Req001 | Method: GET | Path: /index.html
ID: Req002 | Method: POST | Path: /api/login
ID: Req003 | Method: GET | Path: /css/style.css
ID: Req004 | Method: DELETE | Path: /api/users/10
ID: Req005 | Method: PUT | Path: /api/settings
解説
正規表現のグループ化について
正規表現パターン内で () を使用すると、その部分は「キャプチャグループ」として扱われます。re.findall は、パターン内にグループが存在する場合、マッチした文字列全体ではなく、グループにマッチした文字列のタプルをリストとして返します。
- パターン:
r'\[(Req\d+)\]\s+([A-Z]+)\s+(.+)' - 返り値:
[('Req001', 'GET', '/index.html'), ('Req002', 'POST', '/api/login'), ...]
これにより、split メソッドなどで文字列を分割して処理するよりも、安全かつ簡潔にデータを構造化できます。
注意点
re.findallは重複のない全てのマッチをリストで返します。- パターン内にグループが1つだけの場合は、タプルではなく文字列のリストが返されます。複数の項目を抽出したい場合は、上記コードのように複数のグループを定義します。
