note.nkmk.me

Pythonの正規表現マッチオブジェクトでマッチした文字列や位置を取得

Date: 2019-06-09 / tags: Python, 文字列操作, 正規表現

Pythonの正規表現モジュールreのmatch()search()は、文字列が正規表現パターンにマッチした場合、マッチした部分をマッチオブジェクトとして返す。マッチオブジェクトのメソッドを実行することでマッチした文字列を抽出したりその位置を取得したりできる。

ここではマッチオブジェクトの使い方として、以下の内容について説明する。

  • マッチした位置を取得: start(), end(), span()
  • マッチした文字列を取得: group()
  • 正規表現パターンをグルーピングした場合
    • 各グループの文字列を取得: groups()
    • 任意のグループの文字列・位置を取得
    • ネストしたグループ
    • グループに名前を設定
    • 各グループの文字列を辞書で取得: groupdict()
  • ブール値としての扱い(if文での使い方)

正規表現モジュールreの関数などの使い方については以下の記事を参照。

本記事のサンプルコードでは以下の文字列を例とする。

import re

s = 'aaa@xxx.com'
スポンサーリンク

マッチした位置を取得: 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()でマッチした部分を文字列として取得できる。

print(m.group())
# aaa@xxx.com

print(type(m.group()))
# <class 'str'>

正規表現パターンをグルーピングした場合

正規表現パターンの文字列中の部分を括弧()で囲むと、その部分がグループとして処理される。

m = re.match(r'([a-z]+)@([a-z]+)\.([a-z]+)', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

各グループの文字列をタプルで取得: groups()

マッチオブジェクトのメソッドgroups()で、各グループにマッチした部分の文字列をタプルで取得できる。

print(m.groups())
# ('aaa', 'xxx', 'com')

任意のグループの文字列・位置を取得

グルーピングした場合、group()の引数に数値を指定すると任意のグループの文字列を取得できる。省略または0を指定するとマッチ全体、1以降の数値を指定すると順番に各グループの文字列が返される。グループの数より大きい値を指定するとエラー。

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()と同様。ただし、start(), end(), span()では複数の値を指定できない。

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()で複数指定する場合、同時に使ってもOK。

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)を取得できる。

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()で文字列で判定すればよい。

スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事