贪婪在JavaScript中的表现不同?

贪婪在JavaScript中的表现不同?,javascript,regex,regex-greedy,Javascript,Regex,Regex Greedy,这让我意识到,在某些正则表达式引擎中,量词的贪婪程度并不总是相同的。从该问题中提取正则表达式并对其进行一些修改: !\[(.*?)*\] (我知道*在这里是多余的,但我发现下面的行为非常有趣) 如果我们试图与之匹配: ![][][] 我希望第一个捕获组为空,因为(.*?是惰性的,它会在遇到第一个]时停止。这确实是发生在: 但与整个][[() 例如,我环顾了其他一些语言,但它们的行为都与我预期的一样(即返回空的捕获组) (regexplanet的味道显然也得到了非空的捕获组) Java

这让我意识到,在某些正则表达式引擎中,量词的贪婪程度并不总是相同的。从该问题中提取正则表达式并对其进行一些修改:

!\[(.*?)*\]
(我知道
*
在这里是多余的,但我发现下面的行为非常有趣)

如果我们试图与之匹配:

![][][]
我希望第一个捕获组为空,因为
(.*?
是惰性的,它会在遇到第一个
]
时停止。这确实是发生在:

  • 但与整个
    ][[
    ()
例如,我环顾了其他一些语言,但它们的行为都与我预期的一样(即返回空的捕获组)

(regexplanet的味道显然也得到了非空的捕获组)

JavaScript的正则表达式引擎似乎正在解释第二个
*
*?
从懒惰转换为贪婪。注意,将第二个
*
转换为
*?
似乎使正则表达式如我所期望的那样工作(完全删除量词也是如此,因为我知道在这种情况下它是多余的,但这不是重点)

在正则表达式中使用了
*
,但这种行为与
+
{m,n}
类似,将它们转换为惰性版本会得到与
*?
相同的结果

有人知道到底发生了什么吗?

这并不是在回答为什么在Javascript中grediness的行为会有所不同,但它表明这不是一个bug,并且经过了测试。 我将以谷歌的v8引擎为例,我在他们的测试中发现了一个类似的例子

/test/mjsunit/third\u party/regexp pcre.js:

line 1085: res[1006] = /([a]*?)*/;
line 4822: assertToStringEquals("aaaa,a", res[1006].exec("aaaa "), 3176);

这段代码在Javascript中运行良好,但在PCRE php和python中没有相同的输出

更新: 关于你的问题:

JavaScript的正则表达式引擎似乎正在将第二个*转换为*从懒惰转换为贪婪


我做了一些测试,发现在Firefox和Chrome中,如果一个组有一个贪婪的量词,并且直接或间接地包含一个或多个懒惰的量词,并且最小迭代次数为零,那么对于贪婪量词的迭代,在已经满足最小迭代次数的情况下,最左边的懒惰量词可以匹配一个或多个迭代的将匹配一个迭代,如果组将找到零长度匹配,如果惰性量词匹配零迭代

例如,
(.{0,2}?{5,8}
匹配“abcdefghijklmnopqrstuvwxyz”中的“abc”,因为
{0,2}
匹配
{5,8}/code>的迭代6、7和8的一次迭代

如果组后面有贪婪量词无法匹配的标记,则懒惰量词会扩展其迭代次数。从未尝试过零迭代的置换。但贪婪量词仍可以回溯到其最小迭代次数

在同一主题字符串上,
(.{0,3}?{5,6}[ad]
匹配“abcd”,而
(.{0,3}?{5,6})a
匹配“a”

如果组中还有其他内容可以找到匹配项,那么惰性量词的行为与其他正则表达式引擎中的行为相同

当且仅当在带有贪婪量词的组后有不可选的令牌时,Internet Explorer中也会发生同样的情况。如果在组后没有令牌或它们都是可选的,则IE的行为与大多数其他正则表达式引擎一样

Firefox和Chrome中的行为解释似乎是JavaScript标准第15.10.2.5节中的两个步骤的组合。RepeatMatcher的步骤2.1使正则表达式引擎无法对已经匹配了所需最少迭代次数的量词进行零长度迭代,而不仅仅是停止它操作。步骤9在计算懒惰量词本身之前,先计算懒惰量词后面的内容。在这些示例中,包括贪婪量词的持续重复。当贪婪量词在步骤2.1中失败时,懒惰量词将被迫至少重复一次

因此,要回答这是否是一个bug,我会说这是JavaScript规范中的bug。这种行为没有任何好处,并且使JavaScript正则表达式与所有其他(流行的)回溯正则表达式引擎不必要地不同。不过,我不期望未来的JavaScript规范会改变这一点

Opera的行为不同。
({0,2}?{5,8}
匹配“abcd”,而
({0,2}?{6,8}
匹配“abcde”.Opera似乎强制懒惰量词至少为贪婪量词的第一次迭代匹配一次迭代,然后在贪婪量词找到所需的最小迭代次数时停止迭代

我建议不要使用所有内容都是可选的组或备选方案。确保每个备选方案和每个组都匹配某些内容。如果组需要是可选的,请使用
使整个组都是可选的,而不是使组内的所有内容都是可选的。这将减少正则表达式引擎的排列数量必须尝试。

简短回答 该行为在第15.10.2节模式语义中定义,特别是在15.10.2.5节中,其中讨论了产生项::原子量词的语义

作为一个小小的推广:设E为一个可以匹配空字符串的模式。如果存在一个输入字符串S,其中空字符串是E中第一个可匹配的选择,则包含模式E的贪婪重复的模式会受到影响。例如:

(a*?)*              against     aaaa
!\[(.*?)*\]         against     ![][][]
(.*?){1,4}          against     asdf
(|a)*               against     aaaa
(a|()|b){0,4}       against     aaabbb
Firefox和其他基于Webkit的浏览器似乎严格遵循规范,而IE在没有规范的情况下不符合规范
(a*?)*              against     aaaa
!\[(.*?)*\]         against     ![][][]
(.*?){1,4}          against     asdf
(|a)*               against     aaaa
(a|()|b){0,4}       against     aaabbb