文法・環境 組み込み関数

【Python】正規表現の貪欲・非貪欲マッチ

本記事では、正規表現の貪欲(greedy)・非貪欲(non-greedy)マッチについて紹介します。

本記事では、以下の内容を紹介しています。

この記事で分かること

  • 貪欲マッチ、非貪欲マッチについて
  • 貪欲マッチ、非貪欲マッチのサンプルコード

Pythonの正規表現については、以下の記事も併せて参考にしてみてください。

【Python】正規表現の基本的な使用方法【reモジュール】
【Python】正規表現の具体的な使用例【日付、電話番号、メールアドレス、URL】
【Python】正規表現の特殊シーケンスについて【バックスラッシュ、円マーク】
【Python】reモジュールのcompile関数の使い方

スポンサーリンク

【正規表現】貪欲マッチ、非貪欲マッチについて

正規表現には、*、+、?、{m,n}などの繰り返しを表す記号があります。

これらの記号は、デフォルトでは貪欲(greedy)マッチであり、できる限り長い文字列にマッチしようとします

以下は、任意の1文字を表す " . " と、0回以上の繰り返しを表す" * "を組み合わせて、a.*cのパターンを検索するサンプルコードです。

import re

s = r'abc abbbc'

pattern = r'a.*c'
res = re.findall(pattern, s)

print(res)
# ['abc abbbc']

検索結果は、 abc abbbc となり、文字列全体にマッチしています。

これは、貪欲マッチで検索したために、できるだけ長い文字列でマッチされたためです。

では、もし、abc、abbbcを分けて検索したい場合はどうすればよいでしょう。

このような場合に使用するのが、非貪欲マッチ(non-greedy)です。

非貪欲マッチにすると、できるだけ短い文字列にマッチするようになります。

非貪欲マッチを指定する場合は、繰り返しを表す記号の後ろに" ? "を記載します。

以下、非貪欲マッチで検索するサンプルコードです。

import re

s = r'abc abbbc'

pattern = r'a.*?c'
res = re.findall(pattern, s)

print(res)
# ['abc', 'abbbc']

検索結果は、 abcと、abbbc となり、2つの文字列が分かれてマッチしています。

以降では、複数のメタ文字について、貪欲・非貪欲マッチのサンプルコードを紹介します。

スポンサーリンク

【正規表現】貪欲マッチ、非貪欲マッチのサンプルコード

ここから、繰り返しを表すメタ文字について、順番に貪欲・非貪欲マッチのサンプルコードを紹介します。

  • * : 0回以上繰り返し
  • + : 1回以上繰り返し
  • ? : 0回または1回繰り返し
  • {m, n} : m回~n回繰り返し

* : 0回以上繰り返し

貪欲マッチ(再掲)

import re

s = r'abc abbbc'

pattern = r'a.*c'
res = re.findall(pattern, s)

print(res)
# ['abc abbbc']

非貪欲マッチ(再掲)

import re

s = r'abc abbbc'

pattern = r'a.*?c'
res = re.findall(pattern, s)

print(res)
# ['abc', 'abbbc']

+ : 1回以上繰り返し

貪欲マッチ

import re

s = r'abc abbbc'

pattern = r'a.+c'
res = re.findall(pattern, s)

print(res)
# ['abc abbbc']

非貪欲マッチ

import re

s = r'abc abbbc'

pattern = r'a.+?c'
res = re.findall(pattern, s)

print(res)
# ['abc', 'abbbc']

? : 0回または1回繰り返し

貪欲マッチ

import re

s = r'abbb'

pattern = r'ab?'
res = re.findall(pattern, s)

print(res)
# ['ab']

非貪欲マッチ

import re

s = r'abbb'

pattern = r'ab??'
res = re.findall(pattern, s)

print(res)
# ['a']

正規表現の記号" ? "は、0回または1回の繰り返しを表します。

貪欲マッチの場合は、長い文字列にマッチしようとするので、1回の繰り返しでマッチします。

非貪欲マッチの場合は、0回の繰り返しでマッチしています。

{m, n} : m回~n回繰り返し

貪欲マッチ

import re

s = r'abababababab'

pattern = r'(ab){2,4}'
res = re.finditer(pattern, s)

for r in res :
    print(r)
# <re.Match object; span=(0, 8), match='abababab'>
# <re.Match object; span=(8, 12), match='abab'>

非貪欲マッチ

import re

s = r'abababababab'

pattern = r'(ab){2,4}?'
res = re.finditer(pattern, s)

for r in res :
    print(r)
# <re.Match object; span=(0, 4), match='abab'>
# <re.Match object; span=(4, 8), match='abab'>
# <re.Match object; span=(8, 12), match='abab'>

正規表現の記号" {2, 4} "は、2回~4回の繰り返しを表します。

上記のサンプルコードでは、
貪欲マッチの場合は、(ab)のグループについて4回、2回の繰り返し
非貪欲マッチの場合は、(ab)のグループについて2回、2回、2回の繰り返し
でマッチしています。

正規表現に関しては、他にも使用方法をまとめています。併せて参考にしてみてください。

【Python】正規表現の基本的な使用方法【reモジュール】
【Python】正規表現の具体的な使用例【日付、電話番号、メールアドレス、URL】
【Python】正規表現の特殊シーケンスについて【バックスラッシュ、円マーク】
【Python】reモジュールのcompile関数の使い方

スポンサーリンク

-文法・環境, 組み込み関数
-,