1

我拥有的正则表达式是.*MSIE (\d+\.\d+).*(Trident/\d\.\d)?.*

要匹配的字符串:
Mozilla/4.0 (compatible; MSIE 7.0; 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)

所以我希望第 2 组包含值 Trident/5.0。但是出现为空。任何线索我在这里做错了什么?如果我删除?after (Trident/\d\.\d),它将被作为第 2 组拾取。

4

2 回答 2

3

问题

问题是.*前面有可选的(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/\d\.\d)在寻找空字符串之前尝试。如果输入字符串中有模式,它肯定会找到匹配的。如果没有,.*(Trident/\d\.\d)就会失败,我们求助于空字符串。

如果您的引擎支持非捕获组:

.*MSIE (\d+\.\d+)(?:.*(Trident/\d\.\d))?

由于您只需要Trident...,我们无需捕获整个内容。

于 2013-03-20T16:06:38.600 回答
2

你确实解决了这个问题。. . 与“删除?” . . . 如果(Trident/\d\.\d)是可选的,那么与 ..*MSIE (\d+\.\d+).*(Trident/\d\.\d)?.*确实没有什么不同.*MSIE (\d+\.\d+).*

解决这个问题的最简单方法是将其分成两个搜索:MSIE (\d+\.\d+)(Trident/\d\.\d). 您可以进行更复杂的单个匹配,但为了简单起见,您可能希望使用两个单独的匹配。

于 2013-03-20T16:06:48.430 回答