可选表达式未分组

时间:2013-03-20 15:55:36

标签: regex

我的正则表达式是.*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的值。但是即将出现。我有什么问题在这里做错了吗?如果我在?之后删除(Trident/\d\.\d),则会将其作为第2组选中。

2 个答案:

答案 0 :(得分: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...,我们无需捕捉整个事情。

答案 1 :(得分:2)

你实际上已经解决了这个问题。 。 。与“删除?” 。 。 。如果(Trident/\d\.\d)是可选的,则.*MSIE (\d+\.\d+).*(Trident/\d\.\d)?.*.*MSIE (\d+\.\d+).*实际上没有区别。

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