Pythonで文字列のパターンマッチング、検索、置換を行うには、reモジュールを使用します。正規表現は、複雑な文字列処理を簡潔に記述できるツールですが、適切に利用するためには基本的な文法とreモジュールの使い方を理解する必要があります。この正規表現、reモジュールの基本的な使い方とメールアドレスのバリデーション、キーワードによるログの抽出など具体的な使い方・サンプルコードを紹介します。
reモジュールの基本的な使い方
まずはじめに、reモジュールをインポートします。
import re
マッチングの確認: re.search()
re.search()関数は、文字列内にパターンにマッチする部分があるかを調べます。マッチする場合はMatchオブジェクトを返し、マッチしない場合はNoneを返します。
サンプルコード
# シンプルなマッチング text = "The quick brown fox jumps over the lazy dog." pattern = r"fox" # "fox"という文字列にマッチするパターン match = re.search(pattern, text) if match: print("マッチしました:", match.group(0)) # マッチした部分全体を表示 else: print("マッチしませんでした")
re.search()は文字列全体を検索し、最初に見つかったマッチを返します。
文字列の置換: re.sub()
re.sub()関数は、パターンにマッチする部分を別の文字列に置換します。
サンプルコード
# シンプルな置換 text = "The cat is on the mat." pattern = r"cat" # "cat"という文字列にマッチ replacement = "dog" # "dog"に置換 new_text = re.sub(pattern, replacement, text) print(new_text) # The dog is on the mat.
マッチした部分の取得: Matchオブジェクト
re.search()やre.match()が返すMatchオブジェクトからは、マッチに関する様々な情報を取り出せます。
group(): マッチした部分文字列全体、または指定したグループの文字列を取得します。groups(): マッチした部分文字列をグループごとにタプルで取得します。start(): マッチした部分文字列の開始位置を取得します。end(): マッチした部分文字列の終了位置を取得します。
サンプルコード
# 電話番号の抽出 (グループ化) pattern = r"(\d{2,4})-(\d{2,4})-(\d{4})" # 市外局番-市内局番-番号 text = "私の電話番号は03-1234-5678です。" match = re.search(pattern, text) if match: print("電話番号全体:", match.group(0)) print("市外局番:", match.group(1)) print("市内局番:", match.group(2)) print("番号:", match.group(3)) print("すべてのグループ:", match.groups())
主要な正規表現のメタ文字
正規表現では、特殊な記号(メタ文字)を使って、複雑なパターンを表現します。
| メタ文字 | 説明 | 例 |
|---|---|---|
. |
任意の一文字(改行を除く) | a.b |
^ |
文字列の先頭 | ^abc |
$ |
文字列の末尾 | xyz$ |
* |
直前の文字の0回以上の繰り返し | ab*c |
+ |
直前の文字の1回以上の繰り返し | ab+c |
? |
直前の文字の0回または1回の出現 | ab?c |
[ ] |
文字クラス。カッコ内のいずれかの1文字 | [a-z] |
[^ ] |
文字クラスの否定。カッコ内の文字以外の1文字 | [^0-9] |
| |
いずれかのパターン | cat|dog |
( ) |
グループ化。グループは後方参照やMatchオブジェクトから個別にアクセスできる |
(abc){2} |
\d |
数字 | \d{3}(3桁の数字) |
\w |
英数字とアンダースコア [a-zA-Z0-9_] |
\w+ |
\s |
空白文字(スペース、タブ、改行など) | \s+ |
{n,m} |
直前の文字のn回以上m回以下の繰り返し | a{2,4} |
\ |
メタ文字をエスケープする | \. (ドットそのもの) |
文字クラスの詳細
[ ] (文字クラス)を使うと、特定の文字集合のいずれか1文字にマッチさせることができます。
[abc]: 'a', 'b', 'c' のいずれか[a-z]: a から z までのアルファベット小文字[0-9]: 数字[a-zA-Z0-9]: 英数字
文字クラス内で ^ を使うと、指定した文字集合以外の文字にマッチします。
[^0-9]: 数字以外の文字
正規表現の具体例
メールアドレスのバリデーション (簡易版)
完璧なメールアドレスのバリデーションは非常に複雑ですが、簡易的には下記のような正規表現のパターンで可能です。
pattern = r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"
[a-zA-Z0-9._%+-]+:@の前の部分(ユーザー名)。英数字、._%+-の記号を許可し、1文字以上。@: 必須の@記号。[a-zA-Z0-9.-]+:@の後ろの部分(ドメイン名)。英数字、.、-を許可し、1文字以上。\.: ドメイン名とトップレベルドメインを区切る.(\でエスケープ)。[a-zA-Z]{2,}: トップレベルドメイン(.com,.orgなど)。英字2文字以上。
HTMLタグの属性値の取得
HTMLから特定の情報を抽出することにも利用可能です。下記の例はHTMLタグののsrc属性を検索するパターンです。
pattern = r'<[^>]+src="([^"]+)"'
<: HTMLタグの開始[^>]+: 閉じタグ>以外の文字が1文字以上続くsrc=":src属性を検索([^"]+):"で囲まれた内容をキャプチャするためのグループ。[^"]+は"以外の文字が1つ以上続くことを表す": 属性値の終了
サンプルコード
import re html = '<img src="image1.jpg"> <img src="image2.png">' pattern = r'<[^>]+src="([^"]+)"' matches = re.findall(pattern, html) print(matches) # 出力: ['image1.jpg', 'image2.png']
日付の抽出
日付の抽出もよくあるユースケースです。以下は、YYYY-MM-DD 形式に合致するパターンです。
pattern = r"\d{4}-\d{2}-\d{2}"
\d{4}: 4桁の数字(年)-: 年、月、日を区切るハイフン\d{2}: 2桁の数字(月)-: 月、日を区切るハイフン\d{2}: 2桁の数字(日)
テキスト内から日付を抽出するサンプルコード
import re text = "イベントは2024-03-15に開催され、締め切りは2024-03-01です。" pattern = r"\d{4}-\d{2}-\d{2}" dates = re.findall(pattern, text) print(dates) # 出力: ['2024-03-15', '2024-03-01']
特定の単語を含む行を抽出(ログの解析など)
ログの解析などでは、特定のメッセージ、用語を含む行を抽出したいというケースが多々あります。
pattern = r"^.*error.*$"
-
^: 行の先頭 -
.*: 任意の一文字が0回以上 -
error: errorという文字列 -
.*: 任意の一文字が0回以上 -
$: 行の末尾
errorという文字がある行を抽出するサンプルコード
import re text = """ info: this is log error: file not found debug: this code is 100 """ pattern = r"^.*error.*$" result = re.findall(pattern, text, re.MULTILINE) print(result) # ['error: file not found']
より高度な正規表現
re.findall()
re.findall()は、文字列中でパターンにマッチするすべての部分をリストで返します。
サンプルコード
text = "apple, banana, orange, apple, grape" pattern = r"apple" matches = re.findall(pattern, text) print(matches) # ['apple', 'apple']
re.split()
re.split()はパターンにマッチする部分で文字列を分割しリストにします。
サンプルコード
text = "apple, banana, orange, grape" pattern = r",\s*" # カンマとそれに続く空白(0個以上) result = re.split(pattern, text) print(result) # ['apple', 'banana', 'orange', 'grape']
先読み・後読み (Lookahead/Lookbehind)
特定のパターンにマッチする部分の前後の文字列に基づいてマッチを行う、より高度な正規表現のテクニックです。
- 肯定先読み
(?=...):...にマッチする部分の直前にある文字列にマッチ。 - 否定先読み
(?!...):...にマッチしない部分の直前にある文字列にマッチ。 - 肯定後読み
(?<=...):...にマッチする部分の直後にある文字列にマッチ。 - 否定後読み
(?<!...):...にマッチしない部分の直後にある文字列にマッチ。
サンプルコード
# 例: 数字に続く"円"という文字列がある場合に、その数字だけを抽出 text = "これは1000円です。あれは5000円です。" pattern = r"\d+(?=円)" # 肯定先読みを使用 matches = re.findall(pattern, text) print(matches) # ['1000', '5000']
raw文字列 (r"")
Pythonの文字列リテラルでは、\ は特殊文字(エスケープシーケンス)を表すために使われます。正規表現パターンでも\を使うため、バックスラッシュ自体をマッチさせたい場合に、エスケープの重複が発生することがあります。
raw文字列を使うと、文字列中の\を特別扱いせず、そのままの文字として扱えます。正規表現パターンを記述する際は、raw文字列を使うことが推奨されます。
サンプルコード
# raw文字列を使わない場合 (バックスラッシュをエスケープする必要がある) pattern = "\\\\section" # raw文字列を使う場合 (エスケープが不要) pattern = r"\\section"
まとめ
Pythonのreモジュールは、正規表現による強力な文字列処理を提供します。基本的なメタ文字とreモジュールの関数を組み合わせることで、様々な文字列操作を行えます。正規表現は、最初は難しく感じるかもしれませんが、使いこなせるようになると非常に便利です。