为什么这很简单。*?非贪婪的正则表达式贪婪?

时间:2014-12-09 18:15:30

标签: javascript regex non-greedy

我有一个非常简单的正则表达式:

HOHO.*?_HO_

使用此测试字符串......

fiwgu_HOHO_HOHO_HOHOrgh_HOHO_feh_HOHO___HO_fbguyev

  • 我希望它只匹配_HOHO___HO_(最短匹配,非贪婪)
  • 相反,它匹配_HOHO_HOHO_HOHOrgh_HOHO_feh_HOHO___HO_(最长的匹配,看起来贪婪)。

为什么呢?我怎样才能让它与最短的匹配相匹配?

添加和删除?会得到相同的结果。

修改 - 更好的测试字符串,显示[^HOHO]无效的原因:fiwgu_HOHO_HOHO_HOHOrgh_HOHO_feh_HOHO_H_O_H_O_HO_fbguye


所有我能想到的可能是多次匹配 - 但_HO_只有一个匹配,所以我不明白它为什么不参加最短的匹配结束于_HO_,丢弃其余的。

我已经浏览了所有可以找到的标题,例如"非贪婪的正则表达式贪婪",但它们似乎都有其他一些问题。

3 个答案:

答案 0 :(得分:5)

我在Regex lazy vs greedy confusion的帮助下找到了一个解决方案。

在Javascript使用的正则表达式引擎中(我相信NFA engines),非贪婪只会让你从第一个左边开始从左到右最短的匹配 - 适合最近的右手比赛的手牌比赛。

如果一场右手比赛有很多左手比赛,那么它总会从第一场比赛开始(这实际上会给出 最长的 比赛) 。

基本上,它一次通过字符串一个字符询问“这个字符是否匹配?如果是,匹配最短并完成。如果不是,请转到下一个字符,重复”。我希望它是“在这个字符串中的任何地方都有匹配吗?如果是,匹配所有这些中最短的匹配”。


您可以通过将.替换为“不是左侧匹配”的否定来逼近两个方向上非贪婪的正则表达式。到negate a string like this requires negative lookaheads and non-capturing groups,但这就像将字符串放入(?:(?!).)一样简单。例如,(?:(?!HOHO).)

例如,左侧和右侧非贪婪的等效HOHO.*?_HO_将是:

HOHO(?:(?!HOHO).)*?_HO_

因此,正则表达式引擎基本上会遍历每个字符:

  • HOHO - 这与左侧相符吗?
  • (?:(?!HOHO).)* - 如果是这样,我可以到达右侧而不重复左侧吗?
  • _HO_ - 如果是这样,请抓住所有内容直到右侧匹配
  • ?*上的
  • +修饰符 - 如果有多个右手匹配,请选择最近的匹配

答案 1 :(得分:3)

为什么它与整个字符串匹配?

  

这是因为通过查找字符串中可以匹配的第一个位置来完成正则表达式模式匹配。由于可以从字符串的第一个字符开始匹配,因此从不考虑从后续字符开始的较短匹配。

示例:
我们考虑使用正则表达式/a+?b/和测试字符串"aaaaaaaaab"。当应用于字符串时,它匹配整个字符串。不只是上一次a& b。这是因为字符串中可以匹配的第一个位置是第一个a

来源: Javascript:The Definitive Guide,Sixth Edition,页码:255

答案 2 :(得分:1)

结果是非贪婪的,因为它是第一次出现HOHO到达_HO_之前的最短匹配;引擎从左到右穿过字符串,因为它不必回溯,它不会试图缩短任何东西。

要使其按照预期的方式工作,您需要在表达式中使用贪婪的前缀:

/.*(HOHO.*?_HO_)/

第一个内存捕获包含您之后的字符串;贪婪的前缀将尝试跳过尽可能多的字符,因此它将首先匹配HOHO的最后一次出现。