【问题标题】:Nongreedy regex with alternation and repetition [duplicate]带有头韵和重复的非贪婪正则表达式[重复]
【发布时间】:2014-01-12 21:10:47
【问题描述】:

我正在尝试使用扩展正则表达式匹配ABBA 之间的内容,例如使用awk

考虑两个示例字符串AB12BABAAB123BABA,我尝试了以下正则表达式

AB([^B]|([^B][^A]|B[^A]|[^B]A))*BA

但它匹配两个示例的整个字符串(贪婪)。

谁能解释正则表达式引擎在这种情况下是如何工作的,以及我应该如何更改我的正则表达式以使其正常工作。

【问题讨论】:

  • 添加问号 *? 让它变得懒惰
  • @revo 在awk 中不可用我认为?
  • @BastiM No *? 不可用,令牌只能作为*? 单独使用。 *? 的组合在awk 中没有任何特殊的解释(比如将* 的含义改为变得懒惰)..
  • @HåkonHægland 抱歉,在这种情况下我什么都没说消失
  • @TimPietzcker 我希望了解正则表达式引擎的工作原理,这个问题中的示例恰好与另一个问题相似。另一个问题是关于查找正则表达式。这个问题是关于正则表达式引擎如何与重复相结合的交替工作..

标签: regex


【解决方案1】:

BRE 和 ERE 引擎将匹配 Leftmost Longest Rule,这与 Perl 和其他基于 NFA 的正则表达式引擎匹配正则表达式的方式不同。

Boost 库中的documentation 在技术方面更详细,所以我在这里引用它:

最左边最长规则

通常在特定位置匹配正则表达式的方法不止一种,对于 POSIX 基本和扩展正则表达式,“最佳”匹配确定如下:

  1. 找到最左边的匹配项,如果该位置只有一个匹配项,则返回它。
  2. 找出最长的可能匹配项,以及任何关系。如果只有一个这样的可能匹配项,则返回它。
  3. 如果没有标记的子表达式,那么所有剩余的备选方案都无法区分;返回找到的第一个。
  4. 查找与最左边位置的第一个子表达式匹配的匹配项,以及任何关系。如果只有这样的匹配,则返回它。
  5. 查找与第一个子表达式匹配最长的匹配项,以及任何关系。如果只有一个这样的匹配,则返回它。
  6. 对每个附加的标记子表达式重复第 4 步和第 5 步。
  7. 如果仍然存在不止一个可能的匹配项,则它们无法区分;返回找到的第一个。
文中提到的

标记子表达式指的是()捕获组。请注意,它们只进行捕获,不支持反向引用。


因此,为了进行惰性匹配,你需要构造一个正则表达式,使其匹配重复部分,而避免匹配尾部直到最后。由于 ERE 和 BRE 等价于理论上的正则表达式,只要您可以构造 DFA,就存在一个等效的正则表达式可以解决问题(只是在某些情况下构造它并非易事)。

根据您的要求,这个正则表达式应该可以工作:

AB([^B]|B+[^AB])*B*BA

([^B]|B+[^AB])*B* 部分匹配任何不包含字符串“BA”的字符串。

推导

这是匹配不包含字符串“BA”的字符串的 DFA

这里的符号不标准,我稍微解释一下:

  • State q1/B 表示状态被命名为 q1(就像您如何命名变量一样),B 是匹配 BA 的当前进度。
  • * 表示字母表中的任何字符。 [^B] 表示字母表中除 B 之外的任何字符。

在 DFA 中,q0 和 q1 是最终状态,q0 是初始状态。注意 q2 是一个陷阱状态,因为它是一个非最终状态,并且没有从这个状态转移出来。

使用步骤here,或仅使用JFLAP 推导出正则表达式。 (在JFLAP中,你应该使用一些字符,例如C来表示[^AB])。

由于 q2 是陷阱状态,我们可以将其从公式中排除:

R0 =  [^B]R0 + BR1 + λ
R1 = [^AB]R0 + BR1 + λ

将 Arden 定理应用于 R1:

R1 = B*([^AB]R0 + λ)

将 R1 替换为 R0:

R0 = [^B]R0 + BB*([^AB]R0 + λ) + λ

BB* 分发到([^AB]R0 + λ)

R0 = [^B]R0 + BB*[^AB]R0 + BB*λ + λ

组合在一起:

R0 = ([^B] + BB*[^AB])R0 + (BB* + λ)

将 Arden 定理应用于 R0:

R0 = ([^B] + BB*[^AB])*(BB* + λ)

(BB* OR λ (空字符串)) 等价于B*:

R0 = ([^B] + BB*[^AB])*B*

让我们把它改写成awk的语法:([^B]|B+[^AB])*B*,如上所示。

【讨论】:

  • 谢谢!这正是我一直在寻找的:) 你能更详细地解释第 4 步吗?以及它与([^AB]|B|[^B]A)* 有何关系?
  • @HåkonHægland:由于 Tim Pietzcker 的正则表达式不正确,我将扩展我的答案并解释如何获得答案。
【解决方案2】:

使用环视和非贪婪量词:

(?<=AB).*?(?=BA)

如果你也想匹配分隔符,只需:

AB.*?BA

【讨论】:

  • 这个问题是关于 ERE 的。据我所知,没有可用的非贪婪量词..
  • @HåkonHægland 哦。我无法测试 - 你能试着告诉我吗?
  • 是的,我查看了手册gnu.org/software/gawk/manual/gawk.html。它不可用..
  • @HåkonHægland 我在该链接中找不到任何关于贪婪/非贪婪的参考。你确定吗?它说什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多