この記事では、Pythonの正規表現における量指定子(*, +, {}など)が持つ2つのマッチングモード、貪欲マッチと非貪欲マッチの違いについて解説します。
貪欲マッチ(Greedy Matching)
デフォルトでは、正規表現の量指定子は貪欲に動作します。これは、指定された条件の範囲内で、可能な限り最長の部分文字列にマッチしようとすることを意味します。
例えば、<h1>Title</h1>というテキストから<>で囲まれたHTMLタグを抽出しようとする場合を考えます。
import re
text = "<h1>This is a title</h1>"
# `.*`は任意の文字が0回以上続くことを意味する
greedy_pattern = re.compile(r'<.*>')
match_object = greedy_pattern.search(text)
print(match_object.group())
実行結果:
<h1>This is a title</h1>
この場合、.*は最初の<から最後の>まで、できる限り長くマッチしようとするため、意図した<h1>だけでなく、文字列全体がマッチしてしまいます。
非貪欲マッチ(Non-Greedy Matching)
非貪欲モードは、貪欲とは逆に、可能な限り最短の部分文字列にマッチしようとします。量指定子の直後に?を追加することで、非貪欲モードに切り替えることができます。
import re
text = "<h1>This is a title</h1>"
# `*`の後に`?`を追加して非貪欲モードにする
nongreedy_pattern = re.compile(r'<.*?>')
match_object = nongreedy_pattern.search(text)
print(match_object.group())
実行結果:
<h1>
*?は、>に遭遇した時点でマッチを終了させるため、最短である<h1>だけが正しく抽出されます。
繰り返し回数指定での比較
量指定子{n,m}を使った場合も、この違いは明確に現れます。例えば、haが3回から5回繰り返される文字列にマッチさせる場合を考えます。
import re
text = "hahahahaha" # haが5回
# 貪欲マッチ: {3,5}
greedy_pattern = re.compile(r'(ha){3,5}')
greedy_match = greedy_pattern.search(text)
print(f"貪欲マッチの結果: {greedy_match.group()}")
# 非貪欲マッチ: {3,5}?
nongreedy_pattern = re.compile(r'(ha){3,5}?')
nongreedy_match = nongreedy_pattern.search(text)
print(f"非貪欲マッチの結果: {nongreedy_match.group()}")
実行結果:
貪欲マッチの結果: hahahahaha
非貪欲マッチの結果: hahaha
貪欲な{3,5}は、最大の5回にマッチし、非貪欲な{3,5}?は、最小の3回にマッチしていることがわかります。
まとめ
正規表現の量指定子は、デフォルトで貪欲に動作し、可能な限り最長のマッチを試みます。量指定子の後に?を付けることで非貪欲モードに切り替わり、可能な限り最短のマッチを返すようになります。意図した通りの文字列を正確に抽出するためには、この2つのモードを適切に使い分けることが重要です。
