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

Red*_*din 5 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})$))
Run Code Online (Sandbox Code Playgroud)

我在C#中做了一个测试.当我想测试的路径是正确的.结果非常快,这是我想要的.

string path = @"C:\Documents and Settings\jhr\My Documents\Visual Studio 2010\Projects\FileEncryptor\Dds.FileEncryptor\Dds.FileEncryptor.csproj";
Run Code Online (Sandbox Code Playgroud)

=>好的

但是,当我尝试使用我知道不匹配的路径进行测试时,如下所示:

string path = @"C:\Documents and Settings\jhr\My Documents\Visual Studio 2010\Projects\FileEncryptor\Dds.FileEncryptor\Dds.FileEncryptor?!??????";
Run Code Online (Sandbox Code Playgroud)

=> BUG

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

Match match = s_fileRegex.Match(path);
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*ers 10

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

我认为你*在正则表达式中使用得太频繁了.*并不意味着"连接" - 它意味着"0次或更多次".例如,这里不应该有*:

((?<DRIVE>[a-zA-Z]):\\)*
Run Code Online (Sandbox Code Playgroud)

最多应该有一个驱动器规格.你应该?在这里使用,或者如果你想要驱动器规范是强制性的话,根本就没有量词.类似地,正则表达式中似乎存在量词不正确的其他位置.


Tim*_*ker 7

Mark Byers是正确的,因为问题的原因是灾难性的回溯,然而这是导致问题的最后一部分,而不是与驱动器号匹配的位.

例如,在

(?<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})
  $)
)
Run Code Online (Sandbox Code Playgroud)

你可以看到

([a-zA-Z0-9_\s_\-\.]*[a-zA-Z0-9_]+)
|
([a-zA-Z0-9_]+)
Run Code Online (Sandbox Code Playgroud)

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

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

为了解决这个问题,通常需要避免重复元素的重复,以避免与相同事物匹配的交替,并且可能将匹配的部分包含在原子组中,如果正则表达式的后续部分失败则不会回溯到原子组中.

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