Pythonの正規表現マッチオブジェクトでマッチした文字列や位置を取得
Pythonの正規表現モジュールreのmatch()やsearch()は、文字列が正規表現パターンにマッチした場合、マッチした部分をマッチオブジェクトとして返す。マッチオブジェクトのメソッドを実行することでマッチした文字列を抽出したりその位置を取得したりできる。
本記事のサンプルコードでは以下の文字列を例とする。
import re
s = 'aaa@xxx.com'
正規表現モジュールreの関数などの使い方については以下の記事を参照。
マッチした位置を取得: start(), end(), span()
match()やsearch()で文字列が正規表現パターンにマッチするとマッチオブジェクトが返される。
m = re.match(r'[a-z]+@[a-z]+\.[a-z]+', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
print(type(m))
# <class 're.Match'>
マッチオブジェクトのメソッドstart(), end(), span()でマッチした部分文字列の位置(インデックス)を取得できる。
print(m.start())
# 0
print(m.end())
# 11
print(m.span())
# (0, 11)
サンプルコードから分かるように、start()がマッチした部分文字列の先頭、end()が末尾、span()が先頭と末尾のタプルを返す。
マッチした文字列を取得: group()
マッチオブジェクトのメソッドgroup()でマッチした部分を文字列として取得できる。
m = re.match(r'[a-z]+@[a-z]+\.[a-z]+', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
print(m.group())
# aaa@xxx.com
print(type(m.group()))
# <class 'str'>
正規表現パターンをグルーピングした場合
正規表現パターンの文字列中の部分を括弧()で囲むと、その部分がグループとして処理される。
各グループの文字列をタプルで取得: groups()
マッチオブジェクトのメソッドgroups()で、各グループにマッチした部分の文字列をタプルで取得できる。
m = re.match(r'([a-z]+)@([a-z]+)\.([a-z]+)', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
print(m.groups())
# ('aaa', 'xxx', 'com')
任意のグループの文字列・位置を取得
グルーピングした場合、group()の引数に数値を指定すると任意のグループの文字列を取得できる。省略または0を指定するとマッチ全体、1以降の数値を指定すると順番に各グループの文字列が返される。グループの数より大きい値を指定するとエラー。
m = re.match(r'([a-z]+)@([a-z]+)\.([a-z]+)', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
print(m.group())
# aaa@xxx.com
print(m.group(0))
# aaa@xxx.com
print(m.group(1))
# aaa
print(m.group(2))
# xxx
print(m.group(3))
# com
# print(m.group(4))
# IndexError: no such group
group()の引数に複数の数値を指定すると、対応する文字列のタプルが返される。所望のグループのみを選択して取得できる。
print(m.group(0, 1, 3))
# ('aaa@xxx.com', 'aaa', 'com')
start(), end(), span()もgroup()と同様に数値を指定可能だが、複数の数値は指定できない。
print(m.span())
# (0, 11)
print(m.span(3))
# (8, 11)
# print(m.span(4))
# IndexError: no such group
# print(m.span(0, 1))
# TypeError: span expected at most 1 arguments, got 2
ネストしたグループ
グループの括弧()はネストして(入れ子状に)指定することも可能。groups()でマッチ全体の文字列も取得したい場合は全体を括弧()で囲めばよい。グループの順番は左括弧(の順番。
m = re.match(r'(([a-z]+)@([a-z]+)\.([a-z]+))', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
print(m.groups())
# ('aaa@xxx.com', 'aaa', 'xxx', 'com')
グループに名前を設定
括弧()の先頭に?P<xxx>と記述することで、グループに任意の名前xxxをつけることができる。group()やstart(), end(), span()の引数に数値ではなく名前を指定して対応する部分の文字列や位置を取得できるようになる。
m = re.match(r'(?P<local>[a-z]+)@(?P<SLD>[a-z]+)\.(?P<TLD>[a-z]+)', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
print(m.group('local'))
# aaa
print(m.group('SLD'))
# xxx
print(m.group('TLD'))
# com
名前を付けている場合でも数値も使える。group()で複数指定する場合、同時に使ってもよい。
print(m.group(0))
# aaa@xxx.com
print(m.group(3))
# com
print(m.group(0, 2, 'TLD'))
# ('aaa@xxx.com', 'xxx', 'com')
名前を付けてもgroups()の結果は変わらない。
print(m.groups())
# ('aaa', 'xxx', 'com')
各グループの文字列を辞書で取得: groupdict()
groupdict()で名前をキー、文字列を値とする辞書(dict)を取得できる。
m = re.match(r'(?P<local>[a-z]+)@(?P<SLD>[a-z]+)\.(?P<TLD>[a-z]+)', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
print(m.groupdict())
# {'local': 'aaa', 'SLD': 'xxx', 'TLD': 'com'}
print(type(m.groupdict()))
# <class 'dict'>
ブール値としての扱い(if文での使い方)
マッチオブジェクトはブール値として判定される場合は常にTrueとして扱われる。
print(re.match(r'[a-z]+@[a-z]+\.[a-z]+', s))
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
print(bool(re.match(r'[a-z]+@[a-z]+\.[a-z]+', s)))
# True
match()とsearch()ではマッチしない場合はNoneを返す。Noneはブール値としてはFalseと判定される。
print(re.match('[0-9]+', s))
# None
print(bool(re.match('[0-9]+', s)))
# False
したがって、単純にマッチしたかどうかを判定する場合は、match()やsearch()そのまま、または、その返り値をif文で使えばよい。
if re.match(r'[a-z]+@[a-z]+\.[a-z]+', s):
print('match')
else:
print('no match')
# match
if re.match('[0-9]+', s):
print('match')
else:
print('no match')
# no match
正規表現パターンによっては長さ0の文字列(空文字列'')とマッチしてTrueと判定されてしまう場合があるので注意。
m = re.match('[0-9]*', s)
print(m)
# <re.Match object; span=(0, 0), match=''>
print(m.group() == '')
# True
print(bool(m))
# True
if re.match('[0-9]*', s):
print('match')
else:
print('no match')
# match
例のように0回以上の繰り返しを表す*を使うときは要注意。
空文字にマッチしてしまったときもマッチしていないと判定したい場合は、マッチオブジェクトで判定したあとでさらにgroup()で文字列で判定すればよい。