これは非常に古い質問であり、おそらく自分で解決策に興味を持っていないことはわかっていますが、既存の回答は誤解を招く可能性があるため、別の回答を提供したいと思いました。(他の回答の最終的な解決策は、特定の問題に最適です-単一の正規表現ですべてを解決する必要はありません。しかし、回答で指摘された問題は実際には問題ではありませんでした。)
あなたの考えは実際には正しかったのですが、実装は正しくありませんでした。最初の問題は、同じ値が以前に出現したことを保証する肯定的な後読みを使用していることです。(?<!...)
必要なのは、値が表示されていないことを確認するための否定的な後読みです。
ただし、主な問題は、後読みのコンテンツの順序が間違っていることです。コードには、最初にワイルドカードが.*?
あり、次に一致させたいものがありますtag (\k<ID>)
。しかし、現在の位置の左側tag X
のどこかを探しています。つまり、実際にはワイルドカードを 2 番目に配置する必要があります。正規表現エンジンの現在の位置 (後読みの外側) の後ろを見ると、内側のパターンの最後にあります。想像してみてください。これは に一致しますが、 には一致しません。(もっと言えば、もし前に出なければならないのなら、なぜそれ自体が のように反転しないのでしょうか?(?<=abc)def
def
abcdef
cbadef
.*?
tag (\k<ID>)
(\k<ID> gat)
したがって、これは完全に正常に機能します。
(\.\.\. (?<ID>(\d+)))(?<!(?s)tag (\k<ID>).*?)
実際に必要な数よりも多くのグループがありますが。これで十分です:
\.\.\. (?<ID>\d+)(?<!(?s)tag \k<ID>.*?)
ここでテストします。
サイモンのコメントについては、「.NET 正規表現エンジンは文字を左から右に解析するため、右から左に逆参照することはできません。」私はそれについて知りません。私は正規表現エンジンのコードを見たことがありませんが、何年にもわたって .NET の正規表現のフレーバーを実験し、あらゆる種類の悪ふざけのためにそれを悪用してきたことから、すべての実用的な目的で期待されるように、確かにバックトラックしているように見えると言えます。(彼がリンクしたより高速なアルゴリズムは、後方参照を使用しないパターンにのみ適用され、結果は完全に区別できません。) パターンは左から右に一致します。しかし、後読みはパターン内で実際に後読みに到達するまで処理されず、以前のキャプチャがマッチの後半に終わったとしても、それらを逆参照できます。
これに対する 2 つの注意点は、a) .NET には、実際にパターンを右から左に処理する右から左へのマッチング モードがあるため、コード内の後方参照の後にキャプチャを表示する必要があることと、b) 後読みでは前述の右から左を使用することです。 -left モード (これは残念ながら文書化されていません)。これにより、エンジンは、通常の左から右のパターンで後読み (または右から左の横のパターンで先読み、または内部で先読み) を使用して、入力文字列を実際に行ったり来たりできます。後読みとそうでないものがありますが、それはおそらく本番コードでは使用したくないものです)。
入力を処理するこの方向は、後方参照が有効な場合と無効な場合を考慮するときに常に重要です。