web-dev-qa-db-ja.com

正規表現の再帰パターン

これは 外側の括弧と一致する正規表現 に非常に関連していますが、これを行う方法または可能かどうかを具体的に知りたいです regexの再帰パターン私はまだこの戦略を使用したpythonの例を見つけていないので、これは有用な質問にすべきだと思います!

私は seensomeclaimsthat 再帰的なパターンを使用してバランスのとれた括弧に一致させることができますが、Pythonを使用した例はありません regex package(注:redoes notは再帰パターンをサポートしているため、regexを使用する必要があります)。

claim の1つは、構文がb(?:m|(?R))*eであることです。ここで、

bはコンストラクトを開始するもの、mはコンストラクトの中央で発生する可能性のあるもの、eはコンストラクトの最後で発生する可能性のあるものです。


次のouter中括弧に一致するものを抽出します。

"{1, {2, 3}} {4, 5}"
["1, {2, 3}", "4, 5"]  # desired

これはinner中括弧に対しても同じことを簡単に行うことができることに注意してください:

re.findall(r"{([^{}]*)}", "{1, {2, 3}} {4, 5}")
['2, 3', '4, 5']

(私の例では、finditerを使用していました(一致オブジェクト上)、 here を参照してください)

だから、私は次の、またはいくつかのバリエーションが機能することを望んでいた:

regex.findall(r"{(:[^{}]*|?R)}", "{1, {2, 3}} {4, 5}")
regex.findall(r"({(:[^{}]*|?R)})", "{1, {2, 3}} {4, 5}")
regex.findall(r"({(:.*|(?R))*})", "{1, {2, 3}} {4, 5}")
regex.findall(r"({(:.*)|(?R)*})", "{1, {2, 3}} {4, 5}")
regex.findall(r"({(:[^{}])|(?R)})", "{1, {2, 3}} {4, 5}")

しかし、私は[]またはerror: too much backtracking

正規表現の再帰を使用して、外側の括弧の一致オブジェクトを抽出することはできますか?


明らかに、私は撃shotされる危険を冒しています:

私はこれを強調したい再帰パターンの使用方法(私の理解が正しい場合、通常の言語解析の外に連れて行くことができるので、実際に可能です!)。それができれば、これはよりクリーンなソリューションになるはずです。

44
Andy Hayden

パターンは次のとおりです。

{((?>[^{}]+|(?R))*)}

これがあなたの例のために働くのを見ることができます:

regex.findall("{((?>[^{}]+|(?R))*)}", "{1, {2, 3}} {4, 5}")
# ['1, {2, 3}', '4, 5']

説明:

M部分は角かっこを除外する必要があります。同時に[^{}]の数量詞を許可し、致命的なバックトラッキングの問題なしにグループを繰り返したい場合は、アトミックグループの使用が必要です。より明確にするために、最後の閉じ中括弧が欠落している場合、この正規表現エンジンは、文字ごとではなくアトミックグループごとにアトミックグループをバックトラックします。この点を理解するために、次のように量指定子を所有的にすることができます:{((?>[^{}]+|(?R))*+)}(または、原子グループはもはや有用ではないので、{((?:[^{}]+|(?R))*+)})。

アトミックグループ(?>....)と所有量指定子?+*+++は、同じ機能の2つの側面です。この機能は、正規表現エンジンが「アトム」になる文字のグループ内でバックトラックすることを禁止します(小さな部分に分割できないもの)

基本的な例は、文字列aaaaaaaaaabに対して常に失敗する次の2つのパターンです。

(?>a+)ab
a++ab

あれは:

regex.match("a++ab", "aaaaaaaaaab")
regex.match("(?>a+)ab", "aaaaaaaaaab")

(?:a+)またはa+を使用すると、正規表現エンジン(デフォルト)は、すべてのキャラクターのすべてのバックトラック位置を(事前に)記録します。しかし、アトミックグループまたは所有量指定子を使用すると、これらのバックトラック位置は記録されなくなります(グループの開始を除く)。したがって、バックトラッキングメカニズムが発生すると、最後の「a」文字を戻すことはできません。グループ全体のみが付与されます。

[編集]:「展開された」サブパターンを使用して括弧内のコンテンツを記述すると、パターンをより効率的に記述できます。

{([^{}]*+(?:(?R)[^{}]*)*+)}
42

私はこれをb(?:m|(?R))*e構文で問題なく行うことができました:

{((?:[^{}]|(?R))*)}

Demo


あなたがしようとしていたことの鍵は、繰り返しがmではなく、(?:m|(?R))グループ全体で行われることだと思います。これにより、(?R)参照による再帰が可能になります。

10
Sam