我正在测试一些随机正则表达式并且遇到了一些奇怪的结果。假设我们有正则表达式(ab|(ba)*|a)*
它与aba
不匹配,但如果我删除内部星标(ab|(ba)|a)*
或者如果我切换术语的顺序,(a|ab|(ba)*)*
这两个案例现在匹配aba
。那么为什么会这样呢?是否与歧义或嵌套*有关?我知道它是一个奇怪的测试用例,而内部*是多余的,但我只想了解这些结果。我正在使用regex101.com进行测试。
答案 0 :(得分:3)
交替运算符(|
)正在短路,并且将始终尝试匹配最左侧的子模式,直到该模式失败,此时它将尝试匹配下一个子模式。只能匹配非重叠的模式。空字符串匹配导致当前贪婪模式结束,因为空字符串可以无限匹配,并且继续这样做是不合理的,贪婪或不贪婪。贪婪并不一定意味着愚蠢。 :)
因此,对于模式(ab|(ba)*|a)*
和字符串'aba'
,它将匹配字符串开头的“ab”。由于您在最外面的捕获组*上使用贪心量词,因此正则表达式将继续尝试与最外面的捕获组进行更长时间的匹配。匹配迭代器将在第3个字符处,它将尝试匹配'ab',但它将失败。然后,一旦意识到它可能与空字符串无限次匹配(ba)*
,它将结束匹配(不使用(ba)*
和捕获任何内容而不尝试匹配最后一个替代模式a
)并返回最外层重复捕获组的最后一次迭代。
现在,如果你切换与交替运算符链接的子模式的顺序,如(ab|a|(ba)*)*
,那将匹配整个字符串,因为匹配器能够使匹配迭代器前进a
,然后使用第3个替代子模式的最终空字符串匹配完成匹配。
(ab|(ba)|a)*
也有效,因为第二个替代无法与空字符串匹配,因此只要它不匹配ba
,它就会成功转到尝试匹配a
。
修复它的另一种类似方法是使用(ab|(ba)+|a)*
。这将正确地导致第二种替代方法正确失败而不是匹配它。
解决此问题的最后一种方法是将锚点用于字符串的末尾,通常由$
表示。模式(ab|(ba)*|a)*$
能够在匹配第二个替代方案时正确失败,因为它意识到它永远不会通过这样做而到达字符串的末尾。它仍将匹配第二个替代最终,但只有在匹配迭代器遍历到字符串结尾之后才会匹配。
这就是为什么您只能从最外面的捕获组中看到一次捕获字符串'aba'
的原因。模式(ba)*
将始终与索引2-2(或任何空子字符串)匹配,然后结束当前匹配并阻止下一个a
匹配,但除非您将捕获任何内容在您的字符串中有一个明确的'ba'
,它与之前的任何替代品都不重叠。
答案 1 :(得分:2)
您的假设是错误的:它与aba
匹配,请参阅here。
重点是“正则表达式”更喜欢匹配的区别。但是,如果您强制正则表达式从开始到结束匹配,它将完全匹配aba
。
更多细节:如果你使用析取模式(例如r|s
与r
和s
其他正则表达式):正则表达式“喜欢”选择左正则表达式{{1在正确的正则表达式r
上方。例如,如果正则表达式显示s
且输入为(a|aa)*
,则可以匹配第一个项目两次,或使用第二个项目。在这种情况下,正则表达式喜欢选择第一个项目两次。
同样适用于重复,正则表达式希望尽可能多地重复 Kleene星 aa
中的项目。