我有一个正则表达式,只有当字符串在 A 之前的某处包含 B 模式时才会捕获模式 A 。
让我们说,为了简单起见, A 是\b\d{3}\b
(即三位数)而 B 是单词&# 34; 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 是单词&#34; bar&# 34 ;. 我想要构建的是一个表达
的正则表达式(?<= # 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
将无效,因为它会在第一次出现&#34; bar&#34;之后排除所有内容。捕获将是
(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
也不起作用,因为第一个&#34; foo&#34;在我的测试字符串中,它总会找到&#34; bar&#34;作为后缀,它只会捕获
(444, 55)
到目前为止,使用Conditional Matching of Expressions和(现在)知道while inside a lookbehind, .net matches and captures from the right to the left,我能够创建以下正则表达式(?<=(?(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
虽然有效,但过于复杂,因为模式 A , B 和 C 比我在这里发布的示例复杂得多。
是否可以简化此正则表达式?也许使用平衡组?
答案 0 :(得分: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}))+
但是这一次,你需要遍历foo的每一次出现以提取组1中的结果。它不那么方便但是模式更快,因为它不是以交替开始的。
请注意,如果"bar"
和"\d{3}"
以单词字符开头和结尾,您可以更有效地编写模式:
\bfoo(?:\W+(?>(?!bar\b)\w+\W+)*?(\d{3}))+\b
其他方法:将字符串拆分为“foo”和“bar”(保留分隔符),循环遍历每个部分。当部件为“foo”时,将标志设置为true,当部件为“bar”时将其设置为false,当不是“foo”或“bar”时,如果标志为真则提取数字。
答案 1 :(得分:2)
一个简单的选择与Casimir et Hippolyte的第二种模式非常相似:
foo(?>(?<A>\b\d{3}\b)|(?!bar).)+
foo
(?>
... |(?!bar).)+
- 如果您看到bar
,请停止匹配。(?<A>\b\d{3}\b)
并捕获您沿途看到的所有A。(?>)
,回溯不会弄乱这一点。同样,它可以转换为lookbehind:
(?<=foo(?:(?!bar).)*?)(?<A>\b\d{3}\b)
这样做的好处是只匹配数字。 lookbehind断言A之前有foo
,但没有bar
Working example
这两个假设B和C都有点简单。
答案 2 :(得分: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
同样,可能,但平衡组不是最好的选择,主要是因为我们没有平衡任何东西,只是检查是否设置了标志。