本記事は、過去に他のブログで書いたものを引っ越ししてきた記事になります。
Pythonで正規表現を使ってパターンマッチした文字列を取得したい場合に、よく利用する操作を自分用にまとめました。必要に応じて追記していきます。
正規表現とreモジュール
正規表現とは、テキストパターンを表現するための方法のことです。たとえば部分一致する文字列を探す「部分検索」とか、大文字小文字の区別を無くすような「曖昧検索」をしたい時に役立ちます。
英語ではregular expression、略して「regex」と呼ばれたりします。
参考:正規表現とは | 「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
今回扱うPythonで正規表現を扱うには、re
モジュールを使用します。
テキストパターン検索の基本の流れ
ここからは、正規表現を使ってテキストパターン検索を行う手順について、整理していきます。
まずはPythonのスクリプトでre
モジュールをインポートしたのち、以下の手順で検索します。
re.compile()
関数に正規表現パターン文字列を渡してRegexオブジェクトを生成- Regexオブジェクトの
search()
メソッドに検索対象の文字列を渡してパターンを検索してMatchオブジェクトを取得 - Matchオブジェクトの
group()
メソッドを使ってパターンマッチした文字列を取得
import re regex = re.compile(r'\d\d\d') # Regexオブジェクトを生成(例:3つの数値が並んだパターン) mo = regex.search('警察は110番、消防は119番') # パターンを検索してMatchオブジェクトを取得 print(mo.group()) # パターンマッチした文字列を取得
<実行ログ> 101
各手順での処理を具体的に見ていきます。
【MEMO】
今回まとめたやり方では、正規表現パターン文字列からいったんRegexオブジェクトを生成して、Matchオブジェクトを取得するというやり方をとっていますが、もうちょっと簡単な方法がありました。
re.compile()
関数ではなく、re.match()
やre.search()
といった関数を使うと、正規表現パターン文字列から直接Matchオブジェクトを取得することが可能なようです。
手順が1個減りますね。気軽に正規表現を使いたい場合には、もしかしたらこれらの関数を使う方が良いかもしれません。
今回整理したやり方は、同じ正規表現パターンを繰り返し使いたい時に便利な方法のようです。
1. re.compile()関数でRegexオブジェクトを生成
まず、Regexオブジェクトを生成します。
re.compile()
の引数に正規表現パターンを渡す- 戻り値はRegexオブジェクト
- この際に、正規表現パターンにはraw文字列を使うと、バックスラッシュを使った文字をエスケープせずそのまま表示させることができる(例:
r'\n'
)
import re regex = re.compile(r'\d\d\d') # Regexオブジェクトを生成(例:3つの数値が並んだパターン) print(regex) print(type(regex))
<実行ログ> re.compile('\\d\\d\\d') <class 're.Pattern'>
2. search()メソッドでMatchオブジェクトを取得
次に、パターンを検索してMatchオブジェクトを取得します。
- Regexオブジェクトの
serch()
メソッドに検索対象の文字列を渡す - 戻り値はMatchオブジェクト。パターンマッチした最初の場所を探して返す。マッチするパターンが見つからなければ
None
を返す
import re regex = re.compile(r'\d\d\d') # Regexオブジェクトを生成(例:3つの数値が並んだパターン) mo = regex.search('警察は110番、消防は119番') # パターンを検索してMatchオブジェクトを取得 print(mo) print(type(mo))
<実行ログ> <re.Match object; span=(3, 6), match='110'> <class 're.Match'>
3. group()メソッドでパターンマッチした文字列を取得
最後に、Matchオブジェクトからパターンマッチした文字列を取得します。
- Matchオブジェクトの
group()
メソッドを使うと、パターンマッチした文字列を返す
import re regex = re.compile(r'\d\d\d') # Regexオブジェクトを生成(例:3つの数値が並んだパターン) mo = regex.search('警察は110番、消防は119番') # パターンを検索してMatchオブジェクトを取得 print(mo.group()) # パターンマッチした文字列を取得
<実行ログ> 110
ちなみに、正規表現パターンを()
でグルーピングすることもできます。その場合は、group()
の引数に1, 2, 3, ..., 99とインデックスを渡すことで、グループごとに文字列を取り出すことができるようになります。
また、引数に複数のインデックスをカンマ区切りで指定することで、複数の文字列を取り出すことができます。
Regexオブジェクトのメソッド
findall()メソッドで複数のパターンを検索
パターンの検索の際には、Regexオブジェクトのsearch()
メソッドの他にfindall()
メソッドがあります。それぞれの違いは以下の通りです。
search()
メソッド: 最初に見つかった文字列のMatchオブジェクトを返すfindall()
メソッド: 見つかった全ての文字列のリストを返す(※Matchオブジェクトではないので注意)
import re regex = re.compile(r'\d\d\d') items = regex.findall('警察は110番、消防は119番') print(items)
<実行ログ> ['101', '119']
ちなみに正規表現パターンを()
でグルーピングした場合は、findall()
メソッドの戻り値はタプルのリストとなります。
sub()で文字列を置換する
パターンマッチした文字列を、Regexオブジェクトのsub()
メソッド使って置換することもできます。
sub()
メソッド:第1引数に置き換える文字列、第2引数に検索置換対象の文字列を入れると、文字列を全て置換した結果を返す
import re regex = re.compile(r'(\d)\d\d') # 文字列を置換 after = regex.sub(r'\1??', '警察は110番、消防は119番') print(after)
<実行ログ> 警察は1??番、消防は1??番
ちなみに、Regexオブジェクトに()
を使ってグルーピングした場合、置き換える文字列にグループの番号を使って/1
, /2
などと記述できます。これによりマッチした一部のグループを置換せずそのまま表示させることができます(後方参照と呼ぶ)。
re.compile()に渡すオプションについて
re.compile()
の引数に正規表現パターンを渡すことで、Regexオブジェクトを生成するのでした。この時に、第2引数に以下のようなオプションを渡すこともできます。
- re.VERBOSE: 冗長モード。正規表現パターンの空白文字やコメントを無視してくれるので、コードが見やすくなる
- re.IGNORECASE: 大文字・小文字の区別をなくしてマッチングを行う
- re.DOTALL: ドット文字
.
(ワイルドカード)が改行を含む全ての文字とマッチするようになる
また、これらのオプションを組み合わせる場合は|
でつなぐことができます。
(例: re.IGNORECASE | re.DOTALL
)
たとえば、以下の例は冗長モードのオプションを渡した例です。複雑な正規表現パターンを整列させることができて、コードの可読性が上がります。「退屈なことはPythonに任せよう」のサンプルコードより引用させていただきました。
日本の電話番号用の正規表現
# (市外局番が0から始まる1〜4桁、市内局番が1〜4桁、加入者番号4桁) phone_regex = re.compile(r''' (\d{1,4}|\(\d{1,4}\)) # 市外局番 (\s|-) # 区切り (\d){1,4}) # 3桁の番号 (\s|-) # 区切り (\d{3,4}) # 4桁の番号 (\s*(ext|x|ext.)\s*(\d{2, 5}))? # 内線番号 ''', re.VERBOSE)
参考
- 公式ドキュメント
- 参考ブログ
- 引用させていただいたサンプルコード
- 書籍