3

文字列にA の前のどこかにパターンBが含まれている場合にのみ、パターンAをキャプチャする正規表現があります。

簡単にするために、A\b\d{3}\b(つまり 3 桁の数字)、Bは単語「foo」であるとしましょう。

したがって、私が持っている正規表現は(?<=\b(?:foo)\b.*?)(?<A>\b\d{3}\b).

(?<=               # look-behind
    \b(?:foo)\b    # pattern B
    .*?            # variable length
)
(?<A>\b\d{3}\b)    # pattern A

たとえば、文字列の場合

"foo text 111, 222 and not bar something 333 but foo 444 and better 555"

それはキャプチャします

(111, 222, 333, 444, 555)

新しい要件があり、パターンCが先行するキャプチャを除外する必要があります。 Cが単語「バー」であるとしましょう。私が構築したいのは、表現する正規表現です

(?<=               # look-behind
    \b(?:foo)\b    # pattern B
    ???????????    # anything that does not contains pattern C
)
(?<A>\b\d{3}\b)    # pattern A

したがって、例の文字列では、キャプチャする必要があります

(111, 222, 444, 555)

もちろん、次のようなもの(?<=\b(?:foo)\b.*?)(?<!\b(?:bar)\b.*?)(?<A>\b\d{3}\b)

(?<=               # look-behind
    \b(?:foo)\b    # pattern B
    .*?
)
(?<!               # negative look-behind
    \b(?:bar)\b    # pattern C
    .*?
)
(?<A>\b\d{3}\b)    # pattern A

「バー」の最初の出現後にすべてが除外され、キャプチャが行われるため、機能しません

(111, 222)

正規表現(?<=\b(?:foo)\b(?!.*?(?:\bbar\b)).*?)(?<A>\b\d{3}\b)

(?<=                     # look-behind
    \b(?:foo)\b          # pattern B
    (?!                  # negative lookahead
        .*?              # variable lenght
        (?:\bbar\b)      # pattern C
    )
    .*?                  # variable lenght
)
(?<A>\b\d{3}\b)          # pattern A

テスト文字列の最初の「foo」では、常に「bar」がサフィックスとして検出され、キャプチャのみが行われるため、機能しません

(444, 55)

これまでのところ、式の条件付きマッチングを使用し、(現在)後読みの内部で .net が右から左に一致してキャプチャすることを知っているため、次の正規表現を作成できました。(?<=(?(C)(?!)| (?:\bfoo\b))(?:(?<!\bbar)\s|(?<C>\bbar\s)|[^\s])*)(?<A>\b\d{3}\b)

(?<=                     # look-behind
    (?(C)                # if capture group C is not empty
        (?!)             # fail (pattern C was found)
        |                # else
        (?:\bfoo\b)      # pattern B
    )
    (?:
        (?<!\bbar)\s     # space not preceeded by pattern C (consume the space)
        |
        (?<C>\bbar\s)    # pattern C followed by space (capture in capture group C)
        |
        [^\s]            # anything but space (just consume)
    )*                   # repeat as needed
)
(?<A>\b\d{3}\b)          # pattern A

これは機能しますが、パターンAB、およびCは、ここに投稿した例よりもはるかに複雑であるため、複雑すぎます。

この正規表現を単純化することは可能ですか? たぶん、バランシンググループを使用していますか?

4

3 に答える 3

3

\G前の一致の後の位置に一致するアンカーに基づくパターンを使用できます。

(?:\G(?!\A)|\bfoo\b)(?:(?!\b(?:bar|\d{3})\b).)*(\d{3})

デモ

詳細:

(?:
    \G(?!\A) # contiguous to a previous match and not at the start of the string
  |        # OR
    \bfoo\b  # foo: the condition for the first match
)
(?:(?!\b(?:bar|\d{3})\b).)* # all that is not "bar" or a 3 digit number (*)
(\d{3})

(*)実際の状況でより良いサブパターン (つまり、代替を含む先読みで各文字をテストしないもの) を使用できる場合は、ためらわずにそれを変更してください。(たとえば、文字クラスに基づくもの: [^b\d]*(?>(?:\B[b\d]+|b(?!ar\b)|\d(?!\d\d\b))[^b\d]*)*)


別の方法: .net 正規表現エンジンは繰り返しキャプチャを保存できるため、次のようにも記述できます。

\bfoo\b(?:(?:(?!\b(?:bar|\d{3})\b).)*(\d{3}))+

しかし今回は、グループ 1 の結果を抽出するために、foo が出現するたびにループする必要があります。これはあまり便利ではありませんが、交互に開始しないため、パターンは高速です。

"bar"と が単語文字で"\d{3}"開始および終了する場合は、より効率的な方法でパターンを記述できることに注意してください。

\bfoo(?:\W+(?>(?!bar\b)\w+\W+)*?(\d{3}))+\b

別の方法: 文字列を「foo」と「bar」で分割し (区切り文字を保持)、各部分をループします。部分が「foo」の場合はフラグをtrueに設定し、部分が「bar」の場合はfalseに設定し、「foo」でも「bar」でもない場合はフラグがtrueの場合に数値を抽出します。

于 2016-01-15T15:06:13.710 に答える
2

シンプルなオプションの 1 つは、Casimir et Hippolyte の 2 番目のパターンに非常によく似ています。

foo(?>(?<A>\b\d{3}\b)|(?!bar).)+
  • 皮切りにfoo
  • (?>…<code>|(?!bar).)+ - 見た場合はマッチングを停止しますbar
  • (?<A>\b\d{3}\b)途中で目にするすべての A をキャプチャします。
  • この場合、アトミック グループ(?>)は必要ありません。

実施例

同様に、後読みに変換できます。

(?<=foo(?:(?!bar).)*?)(?<A>\b\d{3}\b)

これには、数字のみを一致させるという利点があります。foo後読みは、 A の前に があることをアサートしますが、 . はありませんbar
実施例

これらは両方とも、B と C がやや単純であることを前提としています。

于 2016-01-15T21:18:06.293 に答える
2

あなたが尋ねたので、グループのバランスを取ることは可能ですが、おそらく必要ありません。

\A                    # Match from the start of the string
(?>                   # Atomic group. no backsies.
    (?<B>(?<-B>)?foo)            # If we see "foo", push it to stack B.
                                 # (?<-B>)? ensures B only has one item - if there are two,
                                 # one is popped.
    |(?<-B>bar)                  # When we see a bar, reset the foo.
    |(?(B)(?<A>\b\d{3}\b)|(?!))  # If foo is set, we are allowed to capture A.
    |.                           # Else, just advance by one character.
)+
\z                    # Match until the end of the string.

実施例

さらに賢くしたい場合 (おそらくそうではないでしょう)、ほとんどの分岐を条件分岐に結合できます。

\A
(?>
  (?(B)
    (?:(?<A>\b\d{3}\b)|(?<-B>bar))
    | # else
    (?<B>foo)
  )
  |.
)+
\z

実施例

繰り返しますが、これは可能ですが、主にフラグが設定されているかどうかを確認するだけで、何も分散していないため、グループの分散はここでは最適なオプションではありません。

于 2016-01-15T21:50:31.343 に答える