问题
问题是.*
前面有可选的(Trident/\d\.\d)
. (Trident/\d\.\d)
正则表达式引擎在放弃并将可选组匹配为空字符串之前不会尝试检查是否有任何匹配。
此跟踪将演示正则表达式引擎的工作原理:
匹配后.*MSIE (\d+\.\d+)
,剩余文本为:
; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)
.*
是贪心的,所以它会将所有内容匹配到字符串的末尾。没有剩余文字。
(Trident/\d\.\d)?
是贪心的,所以它会先尝试匹配Trident/\d\.\d
,但失败了。但是,它可以匹配空字符串(空字符串可以无处不在,甚至在行尾)。所以这部分匹配空字符串。
.*
也将匹配空字符串,因为我们在行尾。
在中间更改.*
为惰性量词,即.*MSIE (\d+\.\d+).*?(Trident/\d\.\d)?.*
由于相同的原因将不起作用:
匹配后.*MSIE (\d+\.\d+)
,嗯,同样的事情。
.*?
是懒惰的,所以它会先尝试空字符串。剩下的文字和上面一样(什么都不消耗)。
(Trident/\d\.\d)?
是贪婪的,它再次尝试(Trident/\d\.\d)
首先,失败,它与空字符串匹配。
.*
匹配字符串的其余部分,从.*MSIE (\d+\.\d+)
离开的地方开始。
解决方案
为了强制引擎(Trident/\d\.\d)
在采取简单的方法之前进行检查,我们可以将整个选项设为.*(Trident/\d\.\d)
可选。这将提示引擎检查所有匹配的可能性(Trident/\d\.\d)
,然后放弃并满足于空字符串。
.*MSIE (\d+\.\d+)(.*(Trident/\d\.\d))?
跟踪正则表达式:
如果您的引擎支持非捕获组:
.*MSIE (\d+\.\d+)(?:.*(Trident/\d\.\d))?
由于您只需要Trident...
,我们无需捕获整个内容。