正则表达式匹配需要很长时间才能执行
我编写了一个正则表达式,将文件路径解析为不同的组(DRIVE,DIR,FILE,EXTENSION)。
^((?[a-zA-Z]):\\)*((?[a-zA-Z0-9_]+(([a-zA-Z0-9_\s_\-\.]*[a-zA-Z0-9_]+)|([a-zA-Z0-9_]+)))\\)*(?([a-zA-Z0-9_]+(([a-zA-Z0-9_\s_\-\.]*[a-zA-Z0-9_]+)|([a-zA-Z0-9_]+))\.(?[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";
=>好的
但是当我尝试使用我知道不匹配的路径进行测试时,像这样:
string path = @"C:\Documents and Settings\jhr\My Documents\Visual Studio 2010\Projects\FileEncryptor\Dds.FileEncryptor\Dds.FileEncryptor?!??????";
=> BUG
当我调用这部分代码时,测试会冻结
Match match = s_fileRegex.Match(path);
当我查看我的Process Explorer时,我看到QTAgent32.exe进程挂在处理器的100%处。 这是什么意思 ?
您遇到的问题称为灾难性回溯 ,这是由于正则表达式可以匹配字符串开头的大量方法,由于.NET中的回溯正则表达式引擎而导致性能降低。
我认为你在正则表达式中经常使用*
。 *
并不意味着“连接” – 它意味着“0次或更多次”。 例如,这里不应该有*
:
((?[a-zA-Z]):\\)*
最多应该有一个驱动器规格。 你应该用?
相反,如果你想要驱动器规范是强制性的,或者根本没有量词。 类似地,正则表达式中似乎存在量词不正确的其他位置。
Mark Byers是正确的,因为问题的原因是灾难性的回溯 ,然而这是导致问题的最后一部分,而不是与驱动器号匹配的位。
例如,在
(? ([a-zA-Z0-9_]+ ( ([a-zA-Z0-9_\s_\-\.]*[a-zA-Z0-9_]+) | ([a-zA-Z0-9_]+) )\. (?[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%。
为了解决这个问题,通常需要避免重复元素的重复,以避免与相同事物匹配的交替,并且可能将匹配的部分包含在primefaces组中 ,如果正则表达式的后续部分失败则不会回溯到primefaces组中。
但是,在您的情况下,最好使用正确的工具来完成工作,这些是.NET的路径操作function。
为什么不直接使用System.IO.Path
函数?
-
驱动器: http : //msdn.microsoft.com/en-us/library/system.io.path.getpathroot.aspx
-
目录: http : //msdn.microsoft.com/en-us/library/system.io.path.getdirectoryname.aspx减去Drive的内容
-
文件名: http : //msdn.microsoft.com/en-us/library/system.io.path.getfilenamewithoutextension.aspx
-
扩展: http : //msdn.microsoft.com/en-us/library/system.io.path.getextension.aspx
我只是使用FileInfo
和Path
类来获取信息。
如果您选择使用正则表达式,请注意正则表达式与所有合法文件名都不匹配:正则表达式中缺少一大堆合法的文件名令牌。