为什么Regex IsMatch()挂起

本文关键字:挂起 IsMatch Regex 为什么 | 更新日期: 2023-09-27 18:16:12

我有一个表达式来验证电子邮件地址:

string REGEX_EMAIL = @"^'w+(['.'!'#'$'%'&'''*'+'-'/'='?'^'`'{'¦'}'~]*'w+)*@'w+(['.'-]'w+)*'.'w+(['.'-]'w+)*$";

如果地址正确,IsMatch()方法会快速显示真实的结果。但如果地址字符串长且错误,这个方法会挂起。

我能做些什么来提高这个方法的速度?

谢谢。

为什么Regex IsMatch()挂起

您正在经历灾难性的回溯。

简化的正则表达式:

Regex regexObj = new Regex(@"^'w+([-.!#$%&'*+/=?^`{¦}~]*'w+)*@'w+([.-]'w+)*'.'w+([.-]'w+)*$");

有潜在的问题,例如([.-]'w+)*'.

如果.缺失,例如,您在它之前有一长串字符,那么必须考虑到所有可能的组合,以便您的regex发现它实际上失败了。

在这个正则表达式中,有一些事情正在影响性能。

    灾难性回溯
  1. 可选语句太多

通过在几个关键位置使用+而不是*,您肯定可以提高性能,但这当然会改变正则表达式将匹配和不匹配的内容。因此,我找到的最简单的修复方法实际上在上面的灾难性回溯文章中有介绍。在这种情况下,您可以使用非回溯子表达式来大幅提高性能,而无需以任何重要的方式更改正则表达式的行为。

非回溯子表达式看起来像这样…(?>pattern)

所以试试这个正则表达式:

^'w+(?>['.'!'#'$'%'&'''*'+'-'/'='?'^'`'{'¦'}'~]*'w+)*@'w+(['.'-]'w+)*'.'w+(['.'-]'w+)*$

在一个稍微相关的话题上,我检查有效电子邮件地址的理念有点不同。首先,您已经发现,像这样的长正则表达式可能存在性能问题。

其次,即将到来的电子邮件地址国际化的承诺使这一切变得更加复杂。

最后,任何基于正则表达式的电子邮件验证的主要目的是捕获打字错误和在不输入真实电子邮件地址的情况下通过表单的公然尝试。但是要检查一个电子邮件地址是否真实,需要你向该地址发送电子邮件。

所以我的哲学是宁可接受太多,也不要犯错。事实上,这是一件非常简单的事情……

^.+@.+'..+$

这应该匹配任何可能有效的电子邮件地址,以及一些无效的。

所以,有一些回溯问题。您可以通过谨慎地使用独立的子表达式结构来减少这些问题,但是您仍然会遇到问题,因为内部表达式没有这种约束。最好的办法是把主要部分分开。

把它改成这样很有帮助(展开):

^
  (?>
     'w+
     (
       ['.'!'#'$'%'&'''*'+'-'/'='?'^'`'{'¦'}'~]*
       'w+
     )*
     @
     'w+
   )
   (?>
     (['.'-]'w+)*
     '.
     'w+
     (['.'-]'w+)*
   )
$

但是,如果您通过放置一些合适的断言来重构等效表达式,然后重新添加独立的子表达式分组,则实际上可以消除回溯。在我的regex dubugger中运行这个代码,可以看到只需要很少的步骤就可以通过或失败(展开):

^
  (?>
     'w+
     ['.'!'#'$'%'&'''*'+'-'/'='?'^'`'{'¦'}'~'w]*
     (?<='w)
     @
     'w+
  )     
  (?=.*'.'w)
  (?>    
     (['.'-]'w+)+
  )
$