[解決!Python]re.search/re.match関数と正規表現を使って文字列から部分文字列を抽出するには解決!Python

Pythonの正規表現モジュール(re)が提供するre.search/re.match関数を使って、文字列からパターンにマッチした部分を抽出する方法を紹介する。re.Matchオブジェクトも簡単に取り上げる。

» 2024年01月08日 05時00分 公開
[かわさきしんじDeep Insider編集部]

この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。

「解決!Python」のインデックス

連載目次

* 本稿は2021年3月9日に公開された記事をPython 3.12.1で動作確認したものです(確認日:2024年1月8日)。


import re

s = 'id: deep, mail: deep@foo.com, tel: 03-0123-4567'

# re.search関数は文字列にパターンとマッチする部分があるかを調べる
m = re.search('tel: [-\d]+', s)
print(m)  # <re.Match object; span=(30, 47), match='tel: 03-0123-4567'>
r = m.group()  # re.Matchオブジェクトのgroupメソッドでマッチ全体を抽出
print(r)  # tel: 03-0123-4567

# re.match関数は文字列の先頭とパターンがマッチするかを調べる
m = re.match('tel: [-\d]+', s)  # 「tel: 03-……」は先頭にはないので
print(m)  # これはNoneとなる

m = re.match('\w+: [-\w@.]+', s)  # 文字列先頭が「\w+: [-\w@.]+」にマッチするか
print(m)  # <re.Match object; span=(0, 8), match='id: deep'>
r = m.group()
print(r)  # id: deep

# re.search関数が返すのは最初にマッチした部分を表すre.Matchオブジェクト
m = re.search('\w+: [-\w@.]+', s)
print(m)  # <re.Match object; span=(0, 8), match='id: deep'>
print(m.group())  # id: deep

# 全てのre.Matchオブジェクトを取得するにはre.finditer関数を使う
m_iter = re.finditer('\w+: [-\w@.]+', s)
for m in m_iter:
    print(m.group())
# 出力結果:
# id: deep
# mail: deep@foo.com
# tel: 03-0123-4567

# re.Matchオブジェクトのメソッドを使って抽出する文字列をカスタマイズする
m = re.match('id: \w+', s)

start = m.start()  # マッチ全体の開始位置を取得
end = m.end()  # マッチ全体の終了位置を取得
print(f'start: {start}, end: {end}'# start: 0, end: 8
print(f'span of match: {s[start:end]}'# span of match: id: deep

start, end = m.span()  # マッチ全体の開始位置と終了位置を取得
print(f'span of match: {s[start:end]}'# span of match: id: deep

m = re.match('id: (\w+)', s)  # グループを指定
r = m.group()  # マッチ全体を取得
print(r)  # id: deep
r = m.group(1# 最初のグループを取得
print(r)  # deep


re.search関数とre.match関数

 Pythonが標準で提供する正規表現モジュール(re)には、文字列から指定したパターンにマッチする部分を抽出するのに使える関数が幾つか用意されている。本稿ではその中からre.search関数とre.match関数を主に取り上げる。なお、re.findall関数を使って、文字列から部分文字列を抽出する方法については「[解決!Python]re.findall関数と正規表現を使って文字列から部分文字列を抽出するには」を参照されたい。

 re.search関数とre.match関数はいずれも指定したパターンにマッチする部分が文字列にあるかを調べて、マッチしたらそれを表すre.Matchオブジェクトを返す。ただし、re.search関数はマッチする最初の場所を文字列全体から探すのに対して、re.match関数は文字列先頭から探す(文字列先頭とパターンがマッチするかどうかを調べる)点が異なる。文字列が特定のパターンを含んでいるかどうか調べて該当する部分を抽出するならre.search関数を使い、文字列の先頭が特定のパターンとマッチするかを調べるならre.match関数を使うようにするとよい。

 以下に例を示す。

import re

s = 'id: deep, mail: deep@foo.com, tel: 03-0123-4567'

# re.search関数は文字列にパターンとマッチする部分があるかを調べる
m = re.search('tel: [-\d]+', s)
print(m)  # <re.Match object; span=(30, 47), match='tel: 03-0123-4567'>
r = m.group()  # re.Matchオブジェクトのgroupメソッドでマッチ全体を抽出
print(r)  # tel: 03-0123-4567

# re.match関数は先頭から調べるので以下はNoneが返される
m = re.match('tel: [-\d]+', s)
print(m)  # None


 この例では、re.search関数とre.match関数に同じパターン「tel: [-\d]+」を渡している。これは「tel: 」に続けて連続する数字とマイナス記号を表す。検索対象の文字列は「'id: deep, mail: deep@foo.com, tel: 03-0123-4567'」なので、「tel: 03-1234-5678」はこれにマッチする。マッチした部分の文字列はre.Matchオブジェクトのgroupメソッドで抽出できる。

 re.search関数は文字列全体で最初にマッチする部分を探すので、これを見つけられる。しかし、re.match関数は文字列先頭とパターンを比較するので、Noneが返される。

 以下は、re.match関数で先頭と「\w+: [-\w@.]+」というパターンがマッチする例だ。

m = re.match('\w+: [-\w@.]+', s)
print(m)  # <re.Match object; span=(0, 8), match='id: deep'>
r = m.group()
print(r)  # id: deep


 このパターン「\w+: [-\w@.]+」は、「連続する英数字: 連続するマイナス記号か英数字か@か.」を表すので、「id: deep」「mail: deep@foo.com」「tel: 03-1234-5678」にマッチする。ただし、re.match関数は文字列の先頭部分がパターンにマッチするかどうかを調べるので、これが返送するのはあくまでも「id: deep」を表すre.Matchオブジェクトとなる。

 同じパターンをre.search関数に渡すと、やはり「id: deep」を表すre.Matchオブジェクトが返される。

m = re.search('\w+: [-\w@.]+', s)
print(m)  # <re.Match object; span=(0, 8), match='id: deep'>
r = m.group()
print(r)  # id: deep


 re.search関数ではある文字列の中にマッチする箇所が複数あっても、最初にマッチする部分を表すre.Matchオブジェクトしか返されないことには注意しよう。複数のマッチをre.search関数やre.match関数と同じく、re.Matchオブジェクトの形で取得するにはre.finditer関数を使用する必要がある(複数のマッチを文字列として取得するにはre.findall関数を使用できる)。

 以下に例を示す。

m_iter = re.finditer('\w+: [-\w@.]+', s)
for m in m_iter:
    print(m.group())
# 出力結果:
# id: deep
# mail: deep@foo.com
# tel: 03-0123-4567


 この例では先ほどと同じパターンを、re.finditer関数に渡している。戻り値はマッチした部分に対応するre.Matchオブジェクトを反復するイテレータとなることには注意しておこう。

抽出する文字列のカスタマイズ

 これまでに見てきたように、マッチした部分の文字列を抽出するにはre.Matchオブジェクトのgroupメソッドを使うのが簡単だが、正規表現でグループを指定して、その部分だけを抽出したいこともあるだろう。こうしたときには、groupメソッドに引数を渡したり、re.Matchオブジェクトの他のメソッドを使って、マッチした箇所のインデックスを取得したりしてもよい。

 ここでは詳しい説明は省略するが、re.Matchオブジェクトには以下のようなメソッドがある(一部を抜粋)。

  • startメソッド:マッチの開始位置を取得。引数に整数を指定すると対応するグループの開始位置を取得
  • endメソッド:マッチの終了位置を取得。引数に整数を指定すると対応するグループの終了位置を取得
  • spanメソッド:マッチの開始位置と終了位置をタプルで取得。引数に整数を指定すると対応するグループの開始位置と終了位置をタプルで取得
  • groupメソッド:マッチした部分を文字列として取得。引数に整数を1つ指定すると、対応するグループを文字列として取得。整数を複数指定すると、対応するグループの文字列を格納するタプルを取得
  • groupsメソッド:全てのグループの文字列を格納するタプルを取得

 この他にもさまざまなメソッドや属性があるが、それらについてはPythonの公式ドキュメントにある「マッチオブジェクト」を参照されたい。

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。