当它试图与一个长而完整的字符串匹配时,我有一个正则表达式挂起。这是一个示例控制台应用程序:
using System;
using System.Diagnostics;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
Stopwatch sw;
string pattern = @"(?:(?:https?|ftps?):\/\/)?(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?";
string input = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
Console.WriteLine("Press any key to match regex.");
Console.ReadKey();
Console.WriteLine("Starting regex...");
sw = Stopwatch.StartNew();
Match m = Regex.Match(input, pattern, RegexOptions.IgnoreCase | RegexOptions.Multiline);
sw.Stop();
Console.WriteLine($"Regex completed in {sw.Elapsed}. Press any key to exit.");
Console.ReadKey();
}
}
正则表达式用于在用户生成的注释中查找URL。提供普通评论后,它将立即处理。一个100字的lorem ipsum大约需要36ms的处理时间。一旦引入了一个长而完整的字符串,正则表达式就会挂起,据我所知,它永远不会完成处理。该字符串不需要重复相同的字符。
任何帮助或见识将不胜感激。
答案 0 :(得分:3)
您的正则表达式的主要问题是,在*
/ +
量化组中有一些可选模式以及强制性一种模式,请参见(?:[a-z\u00a1-\uffff0-9]+-?)*
。当正则表达式引擎开始尝试所有可能的路由来匹配字符串时,这可能导致(带有长的不匹配字符串)行为,并且可能会出现太多的路由,以至于系统似乎死机了:灾难性的回溯
因此,如果您打算使用简化的解决方案,则应避免使用类似的模式,请使用
(?:(?:https?|ftp)://)(?:-\.)?[^\s/?.#-]+(?:\.[^\s/?.#-]+)*(?:/\S*)?
其中(?:[^\s/?\.#-]+\.?)+
被展开为[^\s/?.#-]+(?:\.[^\s/?.#-]+)*
。虽然时间更长,但是与可选模式位于量化组内部相比,引擎的故障要快得多。
如果要修复原始图案,请使用
string pattern = @"(?:(?:http|ftp)s?://)?(?:\S+(?::\S*)?@)?(?:(?!1(?:0|27)(?:\.\d{1,3}){3})(?!1(?:69\.254|92\.168|72\.(?:1[6-9]|2\d|3[0-1]))(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:[a-z\u00a1-\uffff0-9]+(?:-[a-z\u00a1-\uffff0-9]+)*)(?:\.[a-z\u00a1-\uffff0-9]+(?:-[a-z\u00a1-\uffff0-9]+)*)*(?:\.[a-z\u00a1-\uffff]{2,}))(?::\d{2,5})?(?:/\S*)?";
检查this regex matches以及如何(?:[a-z\u00a1-\uffff0-9]+-?)*
作为[a-z\u00a1-\uffff0-9]+(?:-[a-z\u00a1-\uffff0-9]+)*
展开以匹配模式,以便每个后续模式都不能匹配相同的字符。我还合并了一些负面的前瞻性和常见的“后缀”。请注意,(?:\S+(?::\S*)?@)?
保持不变,因为它可能需要匹配所有:
直到其余匹配模式之前的最后:
。