BRE 和 ERE 引擎将匹配 Leftmost Longest Rule,这与 Perl 和其他基于 NFA 的正则表达式引擎匹配正则表达式的方式不同。
Boost 库中的documentation 在技术方面更详细,所以我在这里引用它:
最左边最长规则
通常在特定位置匹配正则表达式的方法不止一种,对于 POSIX 基本和扩展正则表达式,“最佳”匹配确定如下:
- 找到最左边的匹配项,如果该位置只有一个匹配项,则返回它。
- 找出最长的可能匹配项,以及任何关系。如果只有一个这样的可能匹配项,则返回它。
- 如果没有标记的子表达式,那么所有剩余的备选方案都无法区分;返回找到的第一个。
- 查找与最左边位置的第一个子表达式匹配的匹配项,以及任何关系。如果只有这样的匹配,则返回它。
- 查找与第一个子表达式匹配最长的匹配项,以及任何关系。如果只有一个这样的匹配,则返回它。
- 对每个附加的标记子表达式重复第 4 步和第 5 步。
- 如果仍然存在不止一个可能的匹配项,则它们无法区分;返回找到的第一个。
文中提到的
标记子表达式指的是()捕获组。请注意,它们只进行捕获,不支持反向引用。
因此,为了进行惰性匹配,你需要构造一个正则表达式,使其匹配重复部分,而避免匹配尾部直到最后。由于 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*,如上所示。