C# 正则表达式导致C中CPU使用异常高#

C# 正则表达式导致C中CPU使用异常高#,c#,.net,regex,C#,.net,Regex,在最近读到一种被称为“灾难性回溯”的现象后,似乎我自己的正则表达式模式导致了某种CPU问题。我使用这个表达式扫描100k-200k个字符的大型HTML字符串。该模式与IP:port格式的IP地址匹配(例如1.1.1.1:90)。模式如下: private static Regex regIp = new Regex(@"(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\." + @"(25[0-5]|2[0

在最近读到一种被称为“灾难性回溯”的现象后,似乎我自己的正则表达式模式导致了某种CPU问题。我使用这个表达式扫描100k-200k个字符的大型HTML字符串。该模式与IP:port格式的IP地址匹配(例如1.1.1.1:90)。模式如下:

private static Regex regIp = new Regex(@"(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\." +
        @"(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4]" +
        @"[0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}" +
        @"[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])\:[0-9]{1,5}", 
        RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant);
MatchCollection matchCol = regIp.Matches(response);

foreach (Match m in matchCol)
{ 
    doWorkWithMatch(m);
}
表达式的用法如下:

private static Regex regIp = new Regex(@"(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\." +
        @"(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4]" +
        @"[0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}" +
        @"[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])\:[0-9]{1,5}", 
        RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant);
MatchCollection matchCol = regIp.Matches(response);

foreach (Match m in matchCol)
{ 
    doWorkWithMatch(m);
}

在通过这个正则表达式模式运行大约100个字符串之后,它开始阻塞计算机并使用99%的CPU。是否有更合理的方法来构造此表达式以减少CPU使用并避免回溯?我不确定是否会发生回溯,或者这只是太多线程同时执行正则表达式求值的问题-欢迎所有输入。

这个正则表达式看起来设计得很好,如果要达到100%的精度,我看不到任何地方可以改进它。然而,您可以测试一些可能总是有效的更简单的方法是否可以改善结果

\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}

显然,这可能会捕获一些不正确的内容,如999.999.999.999:999。但你必须问问自己,这样不现实的输入是否会发生。如果这确实提高了性能,并且您确信不会像我的示例那样有疯狂的输入,那么使用它并使用更精确的正则表达式来筛选列表。

为什么要使用正则表达式进行解析和验证

您应该使用这个正则表达式来解析字符串

\d+[.]\d+[.]\d+[.]\d+(:\d+)?

然后,您可以通过将ip地址解析为int,然后检查范围来检查ip地址是否具有有效范围这是我用于测试和验证ip地址的正则表达式

我在末尾添加了您的端口测试:

(?:(?:1[0-9]{2}2[0-4][0-9]{25[0-5][1-9][0-9]{0-9])\{3}(:1[0-9]{2}2[0-4][0-9][25[0-5][1-9][0-9]{1-9]):[0-9]{1,5}


我看到您也捕获了所有的单个八位字节,通过使用非捕获的
(?:
语法,您将获得性能提升,然后只需在非数字上拆分已验证的字符串。

也许您可以对结果进行分组,并在稍后对端口进行测试,使其不超过255和65535,也就是说,就像Daniel Gimenez的回答一样,但是有组
(\d{1,3})\(\d{1,3})\(\d{1,3})\(\d{1,3})\(\d{1,3})\:(\d{1,5})
,然后对匹配的组进行测试


在正则表达式中包含如此多的
|
通常是一个坏主意。

不要试图用正则表达式解析像HTML这样的不规则语言-这就是疯狂的方式。不要这样做。从来没有。另找一条路。加入修道院。拿起钩针。除了这个什么都可以!除非在非常特殊的情况下,否则使用正则表达式解析HTML是不可能的。我只是建议您使用另一种方法,因此,如果将HTML解析为文档不适合您,请使用另一种方法。请看一看-我从未使用过它,但在尝试使用.NET解析HTML时,我看到过多次建议使用它。@Tim鉴于op希望匹配的模式,正则表达式是正确的选择。正则表达式适合这个应用程序,因为您根本不关心语言的结构。如果您的搜索需求依赖于html的结构,那么Mike的观点是有效的。语言在您的应用程序中是没有意义的。实际上,这个表达式比Denomale下面列出的答案要慢,而且它需要进一步验证IP地址。谢谢你的努力,但不要抽雪茄。@user1111380谢谢你的反馈。事实上,我很惊讶这会变慢。我必须进一步调查,因为我想知道原因。祝你好运虽然这不是问题的根本原因,但这个特定的正则表达式模式确实为最初发布的表达式提供了显著的性能改进。谢谢你是如何为正则表达式生成漂亮的图形/视觉效果的?!手头的真正问题是试图将一个300k字符的XML文档加载到正则表达式中以匹配HTML锚定标记-我不知道是因为它是XML还是因为它是300k字符导致CPU出错,但它阻塞了程序。很酷,我很高兴这有帮助。为了制作这个图表,我正在使用debuggex.com。尽管它不支持lookbehinds、命名捕获组或原子组,但它仍然便于理解表达式流。还有regexper.com。他们也做得很好,但在你打字时不是实时的。