正则表达式捕获输入块中间的可选组

Posted

技术标签:

【中文标题】正则表达式捕获输入块中间的可选组【英文标题】:Regex to capture an optional group in the middle of a block of input 【发布时间】:2009-01-03 02:13:26 【问题描述】:

我遇到了一个看似非常简单的 RegEx 问题,但我无法让它工作。

假设我有这样的输入:

Some text %interestingbit% lots of random text lots and lots more %anotherinterestingbit%
Some text %interestingbit% lots of random text OPTIONAL_THING lots and lots more %anotherinterestingbit%
Some text %interestingbit% lots of random text lots and lots more %anotherinterestingbit%

输入中有很多重复的块,在每个块中我想捕捉一些始终存在的东西(%interestingbit% 和 %anotherinterestingbit%),但也有一些文本可能会或可能不会出现在-在它们之间(OPTIONAL_THING),如果它在那里,我想捕捉它。

像这样的 RegEx 只匹配其中包含 OPTIONAL_THING 的块(并且命名的捕获有效):

%interestingbit%.+?((?<OptionalCapture>OPTIONAL_THING)).+?%anotherinterestingbit%

所以看起来这只是让整个组可选的问题,对吧?这就是我尝试过的:

%interestingbit%.+?((?<OptionalCapture>OPTIONAL_THING))?.+?%anotherinterestingbit%

但我发现虽然这匹配所有 3 个块,但命名捕获(OptionalCapture)在所有块中都是空的!我如何让它发挥作用?

请注意,每个块中可以有很多文本,包括换行符,这就是我输入“.+?”的原因。而不是更具体的东西。我正在使用 .NET 正则表达式,使用 The Regulator 进行测试。

【问题讨论】:

【参考方案1】:

我的想法与 Niko 的想法相似。但是,我建议放置第二个 .+?在可选组内而不是第一个,如下:

%interestingbit%.+?(?:(?<optionalCapture>OPTIONAL_THING).+?)?%anotherinterestingbit%

这避免了不必要的回溯。如果第一个 .+?位于可选组内,并且搜索字符串中不存在 OPTIONAL_THING,正则表达式在到达字符串末尾之前不会知道这一点。然后它需要回溯,也许相当长,以匹配 %anotherinterestingbit%,正如你所说,它将永远存在。

此外,由于 OPTIONAL_THING 存在时,它始终位于 %anotherinterestingbit% 之前,因此它之后的文本实际上也是可选的,并且更自然地适合可选组。

【讨论】:

【参考方案2】:

为什么要多出一组括号?

试试这个:

%interestingbit%.+?(?<OptionalCapture>OPTIONAL_THING)?.+?%anotherinterestingbit%

或者也许这会起作用:

%interestingbit%.+?(?<OptionalCapture>OPTIONAL_THING|).+?%anotherinterestingbit%

在此示例中,该组捕获 OPTIONAL_THING,或者什么都不捕获。

【讨论】:

不,抱歉,这些都不起作用。它们与我的正则表达式相同,组是可选的 - 所有 3 个块都匹配,但没有捕获 OPTIONAL_THING。 @Evgeny,你确定.+?使通配符“不贪心”?也许你可以试试 .*?而是。 @Evgeny,当您将命名组转换为非命名/编号组时,正则表达式的任何工作是否按预期工作?此外,另一种选择是执行类似 /(currently working regex here|regex without OPTIONAL_THING here)/. @strager,不,无论是否命名都没有区别。大|也不起作用,因为它为上述输入生成 2 个匹配项,第一个匹配项是从第一个块的开头到第二个块的结尾。 对我来说,问题似乎出在第一个非贪婪匹配模式上。您实际上匹配到 OPTIONAL_THING 或什么都没有,所以第一个 .+?立即找到“无”并停止匹配。因为 OPTIONAL_THING 不是紧随其后,第二个 .+?匹配输入的其余部分。对吧..?【参考方案3】:

试试这个:

%interestingbit%(?:(.+)(?<optionalCapture>OPTIONAL_THING))?(.+?)%anotherinterestingbit%

首先有一个非捕获组匹配.+OPTIONAL_THING 或不匹配。如果找到匹配项,则里面有一个命名组,它会为您捕获OPTIONAL_THING。其余部分使用.+?%anotherinterestingbit% 捕获。

[编辑]:我为其他捕获组添加了几个括号,因此现在捕获的组与以下内容匹配:

$1:OPTIONAL_THING 之前的文本或什么都没有 $2 或 $optionalCapture:OPTIONAL_THING 或什么都没有 $3 : OPTIONAL_THING 之后的文本,或者如果没有找到 OPTIONAL_THING,则为 %interestingbit% 和 %anotherinterestingbit% 之间的全文

这是你要找的三个匹配项吗?

【讨论】:

抱歉,这与使用一个大的“|”有同样的问题- 第一个匹配包含两个块,所以总共只有 2 个匹配,而不是 3 个。 Oooops.. 在注意到我的答案上方有一个新答案之前编辑了我的答案。关于 Stack Overflow 的另一件事——答案不是按时间顺序排列的......

以上是关于正则表达式捕获输入块中间的可选组的主要内容,如果未能解决你的问题,请参考以下文章

c# 正则表达式捕获

正则表达式 c# 可选组 - 应该贪婪吗?

在正则表达式的可选部分中捕获的组

匹配正则表达式中的可选斜杠

为啥正则表达式可选非捕获组不作为可选并且搞砸匹配?

使用正则表达式匹配日志文件行时的可选字段