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);
}

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

4

4 回答 4

5

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

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

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

然后您可以通过将 IP 地址解析为 int 然后检查范围来检查该 IP 地址是否具有有效范围

于 2013-06-30T03:37:13.567 回答
2

这是我用于测试和验证 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]|[0-9]):[0-9]{1,5}

在此处输入图像描述

我看到您还捕获了所有单独的八位字节,您将通过使用非捕获(?:...)语法获得性能提升,然后将经过验证的字符串拆分为非数字。

于 2013-06-30T04:00:18.860 回答
2

这个正则表达式看起来设计得很好,如果你想要 100% 的准确度,我看不出有什么地方可以改进它。但是,您可以测试一些可能总是有效的更简单的方法是否可以改善结果。

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

显然,这可能会捕捉到一些不正确的东西,比如 999.999.999.999:999。但是你必须问问自己是否会出现这样的不切实际的输入。如果这确实提高了性能,并且您有理由确定您不会像我的示例那样有疯狂的输入,那么使用它并使用您更准确的正则表达式来剔除列表。

于 2013-06-30T03:32:07.500 回答
0

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

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

于 2013-06-30T09:31:24.607 回答