いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Pythonメモ-95 (正規表現で絶対最大量指定子を指定する)(re, regex, possessive quantifier, 強欲な量指定子, 標準モジュールではサポートされていない)

概要

昨日の

devlights.hatenablog.com

に引き続き、標準モジュール re でサポートされていない正規表現シリーズ。

忘れない内にメモメモ。

絶対最大量指定子って

名前がすごいイカついですが、概念的にはアトミックグループと同じようなものです。

アトミックグループと同様にバックトラックを抑制するために指定します。

例えば

aaaabbbb

という文字列があって

.*b+

というパターンを指定した場合、最初の .* が全体にマッチします。

ですが、そのままでは、のこりの b+ というパターンにマッチできないので

バックトラックして 最後の文字 b を残りのパターンに譲ります。

なので、 .*aaaabbb にマッチして、b+b にマッチします。

絶対最大量指定子を使う場合、同じパターンを

.*+b+

と指定します。こうすると、上の例と同様に最初の .*+ が全体にマッチしますが

絶対最大量指定子が付いている場合、こいつは自分がマッチしたものを譲りません。

(なので、強欲な量指定子とも呼ばれます。)

譲らないので、残りのパターンがマッチしなくて、結果マッチしないとなります。

regex モジュールを使う

絶対最大量指定子も、標準モジュールの re ではサポートされていないので

regex モジュールを利用します。

(venv) $ python -m pip install regex

サンプル

"""
正規表現のサンプルです。

絶対最大量指定子 (possessive quantifier) について
(絶対最大量指定子は、強欲な量指定子ともいう)

REFERENCES:: http://bit.ly/2NW2TAq
             http://bit.ly/2NXU6Ow
             http://bit.ly/2NXUcFS
             http://bit.ly/2NZDm9v
             http://bit.ly/2NXxyNQ
"""
import re

import regex

from trypython.common.commoncls import SampleBase
from trypython.common.commonfunc import pr


class Sample(SampleBase):

    def exec(self):
        # ------------------------------------------------------------------------
        # 絶対最大量指定子 (possessive quantifier) について
        # -----------------------------------------
        # アトミックグループ (atomic groups) と同じ考え方で導入されたのが
        # 「*+」というメタキャラクタが従う「強欲」という考え方。
        #
        # 「*+」は「*」と似ているが、アトミックグループと同様に、マッチした範囲のステートを
        # 破棄してバックトラックをしないようになる
        #
        # アトミックグループと同様に、この絶対最大量指定子も標準モジュール re ではサポート
        # されていない。 regex モジュールではサポートされている。
        # ------------------------------------------------------------------------

        # 標準 re モジュールは 「*+」をサポートしていない
        s = 'aaaabbbb'
        p = r'.*+b+'
        try:
            re.compile(p)
        except re.error as e:
            pr('re.error', '標準モジュール re では 「*+」はサポートされていない', e)

        # regex モジュールは 「*+」をサポートしている
        #   この場合、パターンとして指定している「.*+b+」の「.*+」が aaaabbbb 全体に
        #   マッチするが、まだパターンとして「b+」が残っているためバックトラックしようと
        #   するが、絶対最大量指定子を指定しているため、バックトラックが発生せずにここで
        #   マッチ失敗と判定される。
        r = regex.compile(p)
        m = r.match(s)
        if not m:
            pr('.*+b+', '絶対最大量指定子を使っているのでマッチしない (正解)')

        # パターンから絶対最大量指定子をなくして、「.*b+」とすると当然マッチする
        p = r'.*b+'
        r = regex.compile(p)
        m = r.match(s)
        if m:
            pr('.*b+', '絶対最大量指定子を使っていないのでマッチする (正解)')


def go():
    obj = Sample()
    obj.exec()


if __name__ == '__main__':
    go()

try-python/re05.py at master · devlights/try-python · GitHub

結果は、以下のようになります。

re.error='標準モジュール re では 「*+」はサポートされていない'(multiple repeat at position 2)
.*+b+='絶対最大量指定子を使っているのでマッチしない (正解)'
.*b+='絶対最大量指定子を使っていないのでマッチする (正解)'

過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com

github.com