1

正規表現を使用して、以前の groupA には存在しない、後で発生する groupB で何かを見つけようとしています。A に存在する場合は問題ありませんが、B に存在しない場合は問題ありません。これは、正規表現を使用する必要がある場合、否定的な後読みが必要であることを暗示しているようです。

非常に単純化された例:

文章:

groupA:
tag 789
tag 123

groupB:
Item 1:
... 123
... 456

私はルックアラウンドにはかなり慣れていません。これはすぐに頭に浮かんだこと (または多数のバリエーションのうちの 1 つ) ですが、情報に通じている人は、それが意図したとおりに機能しないことに気付くでしょう。

regex:(\.\.\. (?<ID>(\d+)))(?<=(?s).*?tag (\k<ID>))

私の理想的な目標は、groupA に存在しない groupB のアイテムを一致させることであり、入力を並べ替えることができません。正しい出力例: (提供された正規表現では行われません)

... 456

.NET は変数のルックバック距離をサポートしていますが、明らかに何かが欠けています!

4

2 に答える 2

2

.NET 正規表現エンジンは文字を左から右に解析するため、右から左に逆参照することはできません。グループBに到達するまでに、グループAのすべてのキャラクターはすでに消費されており、対戦することはできません. 正規表現は後戻りしているように見えるかもしれませんが、実際には事前照合または分岐を行っているため、パーサーは逆方向に実行されることはありません。

前方実行パーサーでは、最初にグループ A の項目を照合 (および保持) する必要があります。次に、グループ A に項目が存在しない場合にのみ、グループ B から項目を返します。これは決定論的な有限自動化には複雑すぎます。定数空間では実行できないため、計算します。

正規表現を使用して文字列を逆にし、一致を逆方向に実行することで問題を解決できますが、コードがかなりわかりにくくなる可能性があります。

321 ...
:1 metI
:Bpuorg

321 gat
987 gat
:Apuorg

"((?<id>\d+)(?=\s\.\.\.))(?!.*\k<id>\sgat)"

結果:

"654"

代わりに、次のようにシンプルに保つことをお勧めします。

var groupA = Regex.Matches(text, @"(?<=tag\s)\d+").Cast<Match>().Select(x => x.Value);
var groupB = Regex.Matches(text, @"(?<=\.\.\.\s)\d+").Cast<Match>().Select(x => x.Value);

var bNotA = groupB.Except(groupA);
于 2012-08-07T03:20:35.703 に答える
0

これは非常に古い質問であり、おそらく自分で解決策に興味を持っていないことはわかっていますが、既存の回答は誤解を招く可能性があるため、別の回答を提供したいと思いました。(他の回答の最終的な解決策は、特定の問題に最適です-単一の正規表現ですべてを解決する必要はありません。しかし、回答で指摘された問題は実際には問題ではありませんでした。)

あなたの考えは実際には正しかったのですが、実装は正しくありませんでした。最初の問題は、同じ値が以前に出現したことを保証する肯定的な後読みを使用していることです。(?<!...)必要なのは、値が表示されていないことを確認するための否定的な後読みです。

ただし、主な問題は、後読みのコンテンツの順序が間違っていることです。コードには、最初にワイルドカードが.*?あり、次に一致させたいものがありますtag (\k<ID>)しかし、現在の位置の左側tag Xのどこかを探しています。つまり、実際にはワイルドカードを 2 番目に配置する必要があります。正規表現エンジンの現在の位置 (後読みの外側) の後ろを見ると、内側のパターンの最後にあります。想像してみてください。これは に一致しますが、 には一致しません。(もっと言えば、もし前に出なければならないのなら、なぜそれ自体が のように反転しないのでしょうか?(?<=abc)defdefabcdefcbadef.*?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 モード (これは残念ながら文書化されていません)。これにより、エンジンは、通常の左から右のパターンで後読み (または右から左の横のパターンで先読み、または内部で先読み) を使用して、入力文字列を実際に行ったり来たりできます。後読みとそうでないものがありますが、それはおそらく本番コードでは使用したくないものです)。

入力を処理するこの方向は、後方参照が有効な場合と無効な場合を考慮するときに常に重要です。

于 2016-02-16T14:44:51.960 に答える