C# 正则表达式性能问题

C# 正则表达式性能问题,c#,regex,performance,C#,Regex,Performance,我有一个长字符串,其中包含大约100个参数(string parameterName),与以下模式匹配: parameterName + "(Whitespace | CarriageReturn | Tabulation | Period | Underline | Digit | Letter | QuotationMark | Slash)* DATA Whitespace Hexadecimal" 我曾尝试使用此正则表达式,但它的工作时间太长: parameterName + "[\\

我有一个长字符串,其中包含大约100个参数(string 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]”还要慢


有什么方法可以改进这个正则表达式吗?

您可以使用

(?>(?>[^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
)
想法

我们的想法是在不问自己任何问题的情况下获取
数据,并且不要再进一步,然后返回到它

如果对字符串(如
DATA321
)使用
*DATA
,请参见正则表达式引擎的功能:

  • *
    会吃掉所有字符串
  • 找不到
    数据
    ,因此引擎将逐步回溯并尝试以下组合:
    *
    将只吃
    数据32
    ,然后是
    数据3
    ,然后是
    数据
    。。。然后什么都没有,那就是我们找到对手的时候了
如果在
123DATA
上使用
*?DATA
*?
将尝试不匹配任何内容,然后
1
,然后
12

每次尝试时,我们都必须检查
停止位置之后是否没有
数据
,这非常耗时。有了
[^D]+|D(?!ATA)
我们可以确保在需要的时候停下来,而不是在之前,而不是之后

小心回溯

那么为什么不使用
(?:[^D]| D(?!ATA))
而不是这些奇怪的原子分组呢

当我们找到匹配项时,这一切都很好,工作正常。但如果我们不这样做会发生什么呢?在声明失败之前,正则表达式必须尝试所有可能的组合。当您在每个字符上都有类似于
(.*)*
的内容时,正则表达式引擎可以使用内部
*
或外部

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

你可以理解我的意思:检查一个15个字符长的字符串需要8万个步骤,而这个字符串永远不会匹配


regex大师Friedl在这篇伟大的文章中对此进行了更深入的讨论(比我所能做的更好),

您可以为SPEED.FYI编译您的regex,为了使通配符
匹配新行,您可以只使用
s
标志。我会预编译我的regex,尽管我有每个的循环(parameterNameArray中的字符串parameterName),其中包含每个parameterName的正则表达式编译。感谢您的回答!但是,我尝试使用它时遇到了“嵌套量词异常”。嵌套量词确实会导致“灾难性回溯”,所以你必须使用所有格量词来避免这个问题。我猜C#module无法识别它,并且完全禁止它…只有通过删除++和*+才能解决这个问题。虽然我必须测试它,但它似乎工作得更快。无论如何,谢谢你!@Ivan:请不要这样做,它实际上相当“危险”。我马上解释原因。@Robin.NET不支持所有格量词。它将原始答案中的
[^D]++
解释为
([^D]+)+
;这就是导致“嵌套量词”异常的原因。