正则表达式性能问题

时间:2014-04-09 09:00:52

标签: c# regex performance

我有一个长字符串,其中包含大约100个与以下模式匹配的参数(字符串parameterName):

parameterName + "(Whitespace | CarriageReturn | Tabulation | Period | Underline | Digit | Letter | QuotationMark | Slash)* DATA Whitespace Hexadecimal"

我试过使用这个正则表达式,但它的工作时间太长了:

parameterName + "[\\s\\S]*DATA\\s0x[0-9A-F]{4,8}"

这个凌乱的效果好一点:

parameterName + "(\\s|\r|\n|\t|\\.|[_0-9A-z]|\"|/)*DATA\\s0x[0-9A-F]{4,8}"

我使用“。*”,但是,它与“\ n”不匹配。

我尝试过“(。| \ n)”,但它的工作速度甚至比“[\ s \ S]”还慢。

有没有办法改善这个正则表达式?

1 个答案:

答案 0 :(得分:1)

您可以使用类似

的内容
(?>(?>[^D]+|D(?!ATA))*)DATA\\s0x[0-9A-F]{4,8}

(?>                # atomic grouping (no backtracking)
    (?>            # atomic grouping (no backtracking)
        [^D]+      # anything but a D
    |              # or
        D(?!ATA)   # a D not followed by ATA
    )*             # zero or more time
)

想法

我的想法是在没有问自己任何问题的情况下到达DATA并且不再进一步,然后回溯到它

如果对.*DATA这样的字符串使用DATA321,请查看正则表达式引擎的作用:

  • .*吃掉所有字符串
  • 找不到DATA,因此引擎会一步一步地回溯并尝试这些组合:.*只会吃DATA32,然后DATA3,然后{ {1}} ...然后没有,当我们找到匹配时就是这样。

如果您在DATA上使用.*?DATA,则会发生同样的事情:123DATA会尝试匹配任何内容,然后.*?,然后1 ...

在每次尝试时,我们都必须在12停止的地方之后检查没有DATA,这非常耗时。使用.*,我们确保在需要时准确停止 - 而不是之前,而不是之后。

谨防回溯

那么为什么不使用[^D]+|D(?!ATA)代替这些奇怪的原子分组?

当我们找到匹配项时,这一切都很好并且正常工作。但是当我们不这样做时会发生什么?在声明失败之前,正则表达式必须尝试所有可能的组合。当你在每个角色上都有(?:[^D]|D(?!ATA))之类的东西时,正则表达式引擎可以使用内部(.*)*或外部{。}}。

这意味着组合的数量非常迅速地变得巨大。我们希望尝试所有这些:我们知道我们停在正确的地方,如果我们没有立即找到匹配,我们永远不会。因此原子分组(显然.NET不支持占有量词)。

你可以看到我的意思over here:80'000步检查一个永远无法匹配的15个字符的长字符串。

在这篇伟大的文章中,弗雷德尔,正则表达式大师,over here

进行了更深入的讨论(并且比我做的更好)