为啥用 Regex.IsMatch 检查这个字符串会导致 CPU 达到 100%?

Posted

技术标签:

【中文标题】为啥用 Regex.IsMatch 检查这个字符串会导致 CPU 达到 100%?【英文标题】:Why does checking this string with Regex.IsMatch cause CPU to reach 100%?为什么用 Regex.IsMatch 检查这个字符串会导致 CPU 达到 100%? 【发布时间】:2015-09-22 12:48:31 【问题描述】:

在特定字符串上使用Regex.IsMatch (C#, .Net 4.5) 时,CPU 达到 100%。

字符串:

https://www.facebook.com/CashKingPirates/photos/a.197028616990372.62904.196982426994991/1186500984709792/?type=1&permPage=1

图案:

^http(s)?://([\w-]+.)+[\w-]+(/[\w- ./?%&=])?$

完整代码:

Regex.IsMatch("https://www.facebook.com/CashKingPirates/photos/a.197028616990372.62904.196982426994991/1186500984709792/?type=1&permPage=1",
                @"^http(s)?://([\w-]+.)+[\w-]+(/[\w- ./?%&=])?$");

我发现编辑 URL 可以防止这个问题。编辑网址:

https://www.facebook.com/CashKingPirates/photos/a.197028616990372.62904.196982426994991/1186500984709792

但仍然非常有兴趣了解造成这种情况的原因。

【问题讨论】:

正则匹配涉及大量回溯。更多信息请阅读runaway regular expressions 由于您没有在点前加反斜杠,因此您的正则表达式可能是 ^http(s)?://.+$ 的一个非常复杂的版本(即匹配 http:// 或 https:// 后跟任何内容)。 为什么不用URI类? @SalmanA 在看到潜在危害后,我实际上从正则表达式更改为 URI.TryCreate。 【参考方案1】:

正如 nu11p01n73R 指出的那样,您的正则表达式有很多回溯。那是因为你的表达式的某些部分都可以匹配同一个东西,这给引擎提供了很多选择,它必须在找到结果之前尝试。

您可以通过更改正则表达式以使各个部分更加具体来避免这种情况。在您的情况下,原因是您想匹配一个真实的点,但使用了匹配所有字符 . 。你应该把它转义到\.

这应该已经大大减少了回溯需求并使其快速:

^http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=])?$

而如果要实际匹配原始字符串,则需要在字符类末尾添加一个量词:

^http(s)?://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]+)?$
                                           ↑

【讨论】:

谢谢,但您建议的正则表达式与原始字符串不匹配。不过速度很快。 确实,这是导致灾难性回溯的唯一且确切的原因:点未转义,不被视为文字字符并且可以匹配任何内容。 @Nir:请注意,这完全回答了您的问题。您现在可以轻松地纠正您的模式。【参考方案2】:

我建议你检查http://regexr.com/网站,测试你的正则表达式。

正则表达式的更正版本如下:

^(https?://(?:[\w]+\.?[\w]+)+[\w]/?)([\w\./]+)(\?[\w-=&%]+)?$

它也有3组:

    group1=主要网址(例如:facebook.com) group2=子网址(例如:/CashKingPirates/photos/a.197028616990372.62904.196982426994991/1186500984709792/ group3=变量(例如:?type=1&permPage=1)

还要记住,要检查正则表达式中点 (.) 的实际字符,您必须使用 \。不是。

【讨论】:

我不能告诉你他问题的确切原因,但据我所知 [\w-]+ 在他的正则表达式中是完全没有必要的。 regxer 专为不支持lookbehinds的js设计。【参考方案3】:

您的正则表达式因catastrophic backtracking 而受到影响。您可以简单地使用

^http(s)?://([\w.-])+(/[\w ./?%&=-]+)*$

查看演示。

https://regex101.com/r/cK4iV0/15

【讨论】:

这是一个糟糕的解决方案,会降低原始正则表达式的意图。

以上是关于为啥用 Regex.IsMatch 检查这个字符串会导致 CPU 达到 100%?的主要内容,如果未能解决你的问题,请参考以下文章

c# regex.ismatch 使用变量

有没有办法只使用 Regex.IsMatch 来匹配也有数字的字符串?

regex.ismatch 验证是不是含有特殊字符

在 TextBox.Text 中退格后 Regex.IsMatch 不起作用

c#大于等于3小于等于99正则表达式怎么写? (Regex.IsMatch(textBox1.Text, "[3-9]1,"))

C# winform 检查textbox中是不是含有某字符