量指定子を取得できる正規表現エンジンを知りません。ただし、PCRE または Perl を使用すると、いくつかのトリックを使用して、同じ数の文字があるかどうかを確認することができます。あなたの例では:
@@@@ "スターウォーズ" ==== "1977" ---- "サイエンスフィクション" //// "ジョージ・ルーカス"
有名な Qtax トリック@
=
-
/
を使用するこのパターンでバランスが取れているかどうかを確認できます (準備はできていますか?):
「所有オプションの自己参照グループ」
~(?<!@)((?:@(?=[^=]*(\2?+=)[^-]*(\3?+-)[^/]*(\4?+/)))+)(?!@)(?=[^=]*\2(?!=)[^-]*\3(?!-)[^/]*\4(?!/))~
パターンの詳細:
~ # pattern delimiter
(?<!@) # negative lookbehind used as an @ boundary
( # first capturing group for the @
(?:
@ # one @
(?= # checks that each @ is followed by the same number
# of = - /
[^=]* # all that is not an =
(\2?+=) # The possessive optional self-referencing group:
# capture group 2: backreference to itself + one =
[^-]*(\3?+-) # the same for -
[^/]*(\4?+/) # the same for /
) # close the lookahead
)+ # close the non-capturing group and repeat
) # close the first capturing group
(?!@) # negative lookahead used as an @ boundary too.
# this checks the boundaries for all groups
(?=[^=]*\2(?!=)[^-]*\3(?!-)[^/]*\4(?!/))
~
本旨
非キャプチャ グループには 1 つだけが含まれます@
。このグループが繰り返されるたびに、キャプチャ グループ 2、3、および 4 に新しいキャラクターが追加されます。
所有格オプションの自己参照グループ
それはどのように機能しますか?
( (?: @ (?= [^=]* (\2?+ = ) .....) )+ )
@ 文字が最初に出現した時点では、キャプチャ グループ 2 はまだ定義されていない(\2 =)
ため、パターンが失敗するようなものを記述することはできません。この問題を回避するには、後方参照をオプションにする方法があります。\2?
このグループの 2 番目の側面は、=
一致する文字の数が非キャプチャ グループの繰り返しごとに増加することです。これは、=
毎回追加されるためです。この数が常に増加する (またはパターンが失敗する) ことを保証するために、所有量指定子は、新しい=
文字を追加する前に、まず後方参照を強制的に一致させます。
このグループは次のように表示されることに注意してください。グループ 2 が存在する場合は、次のグループと一致します。=
( (?(2)\2) = )
再帰的な方法
~(?<!@)(?=(@(?>[^@=]+|(?-1))*=)(?!=))(?=(@(?>[^@-]+|(?-1))*-)(?!-))(?=(@(?>[^@/]+|(?-1))*/)(?!/))~
@ 部分を数回使用するため、オーバーラップ マッチを使用する必要があります。これが、すべてのパターンがルックアラウンド内にある理由です。
パターンの詳細:
(?<!@) # left @ boundary
(?= # open a lookahead (to allow overlapped matches)
( # open a capturing group
@
(?> # open an atomic group
[^@=]+ # all that is not an @ or an =, one or more times
| # OR
(?-1) # recursion: the last defined capturing group (the current here)
)* # repeat zero or more the atomic group
= #
) # close the capture group
(?!=) # checks the = boundary
) # close the lookahead
(?=(@(?>[^@-]+|(?-1))*-)(?!-)) # the same for -
(?=(@(?>[^@/]+|(?-1))*/)(?!/)) # the same for /
=
-
前のパターンとの主な違いは、これがと/
グループの順序を気にしないことです。(ただし、文字クラスと否定先読みを使用して、最初のパターンに簡単に変更を加えて、それに対処することができます。)
注: 文字列の例では、より具体的には、否定後読みをアンカー (^
または\A
) に置き換えることができます。そして、文字列全体をマッチ結果として取得したい場合は.*
、最後に追加する必要があります (そうしないと、マッチ結果はふざけて気づいたように空になります)。