正则表达式匹配需要很长时间才能执行

时间:2010-08-04 07:41:32

标签: c# regex performance

我写了一个正则表达式,将文件路径解析为不同的组(DRIVE,DIR,FILE,EXTENSION)。

^((?<DRIVE>[a-zA-Z]):\\)*((?<DIR>[a-zA-Z0-9_]+(([a-zA-Z0-9_\s_\-\.]*[a-zA-Z0-9_]+)|([a-zA-Z0-9_]+)))\\)*(?<FILE>([a-zA-Z0-9_]+(([a-zA-Z0-9_\s_\-\.]*[a-zA-Z0-9_]+)|([a-zA-Z0-9_]+))\.(?<EXTENSION>[a-zA-Z0-9]{1,6})$))

我在C#中进行了测试。当我想测试的路径是正确的。结果很快,这就是我想要的。

string path = @"C:\Documents and Settings\jhr\My Documents\Visual Studio 2010\Projects\FileEncryptor\Dds.FileEncryptor\Dds.FileEncryptor.csproj";

=&GT;行

但是当我尝试使用我知道不匹配的路径进行测试时,就像这样:

string path = @"C:\Documents and Settings\jhr\My Documents\Visual Studio 2010\Projects\FileEncryptor\Dds.FileEncryptor\Dds.FileEncryptor?!??????";

=&GT; BUG

当我调用这段代码时,测试会冻结

Match match = s_fileRegex.Match(path);

当我查看我的Process Explorer时,我看到QTAgent32.exe进程挂在处理器的100%处。这是什么意思?

4 个答案:

答案 0 :(得分:10)

您遇到的问题称为catastrophic backtracking,这是由于正则表达式可以匹配字符串开头的大量方法,由于.NET中的回溯正则表达式引擎而导致性能降低

我认为您在正则表达式中过于频繁地使用**并不意味着“连接” - 它意味着“0次或更多次”。例如,此处不应该有*

((?<DRIVE>[a-zA-Z]):\\)*

最多应该有一个驱动器规格。您应该在此处使用?,否则如果您希望驱动器规范是强制性的,则根本不使用量词。同样,正则表达式中似乎还有其他位置,其中量词不正确。

答案 1 :(得分:7)

Mark Byers是正确的,因为问题的原因是catastrophic backtracking,但这是导致问题的最后一部分,而不是与驱动器号匹配的位。

例如,在

(?<FILE>
  ([a-zA-Z0-9_]+
    (
      ([a-zA-Z0-9_\s_\-\.]*[a-zA-Z0-9_]+)
    |
      ([a-zA-Z0-9_]+)
    )\.
    (?<EXTENSION>[a-zA-Z0-9]{1,6})
  $)
)

你可以看到

([a-zA-Z0-9_\s_\-\.]*[a-zA-Z0-9_]+)
|
([a-zA-Z0-9_]+)

可以通过多种不同的方式匹配相同的字符串,这些方式将随着文件名的长度呈指数增长。

当正则表达式的扩展部分无法匹配时,正则表达式引擎会回溯并尝试对文件名部分进行不同的排列,希望这可以使扩展部分匹配 - 当然它永远不会,但是正则表达式引擎无法弄清楚这一点。 RegexBuddy,当被要求在您提供的路径上测试正则表达式时,在1.000.000次迭代后中止匹配尝试。 C#正则表达式引擎将继续运行,直到它耗尽所有排列,在此期间将CPU固定在100%。

为了解决这个问题,通常需要避免重复元素的重复,以避免与同一事物相匹配的变化,并且可能将匹配的部分包含在atomic groups中,如果以后的部分不会被回溯到正则表达式失败了。

但是,在你的情况下,最好使用正确的工具来完成这项工作,那些是.NET的路径操作功能。

答案 2 :(得分:5)

答案 3 :(得分:0)

我只是使用FileInfoPath类来获取信息。

如果您选择使用正则表达式, 然后请注意,正则表达式与所有合法文件名都不匹配:正则表达式中缺少一大堆合法文件名令牌。