.NET真的使用NFA作为正则表达式引擎吗?

时间:2013-12-12 09:37:37

标签: c# .net regex algorithm

来自MSDN的文章Details of Regular Expression Behavior说,.NET开发人员决定使用正则表达式传统 NFA 引擎,因为它比 POSIX NFA 更快,但是我不清楚,为什么这种模式会以指数方式缓慢起作用呢?

var regex = new Regex("(a|aa)*b");
var b = regex.IsMatch("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac");

这种简单的模式匹配需要超过30分钟才能执行。但是如果.NET使用传统的NFA,可以模拟它并在最坏的情况下找到 O(M * N)时间的匹配,其中M是模式长度,N是文本长度,这肯定是在这种情况下是不正确的。

文章还解释了回溯是执行缓慢的原因,但我仍然有一些无法找到答案的问题

  1. 什么是回溯?是否只使用已经匹配的模式,如(a|b)c/1
  2. 传统的 NFA 是否支持回溯,如果没有需要进行哪些修改?
  3. 如果NFA支持它,但需要更慢的算法来模拟,为什么.NET无法检查模式中是否存在回溯,如果不存在,则使用一种算法并使用另一种算法?

1 个答案:

答案 0 :(得分:2)

您可以将正则表达式编译为NFA或DFA,尽管从NFA计算的DFA可能不切实际。您可以使用或不使用回溯来匹配NFA,但是在没有反向跟踪的情况下工作的方案通常会对正则表达式语言施加更多约束,并且在存在许多可能匹配时会找到匹配项。

您的示例很慢,因为匹配器必须经常决定是否匹配a或aa,以及是否尝试匹配最终b。回溯的工作方式就像运行递归函数一样,在每一步都对每种可能性进行递归调用 - 递归地匹配a,如果失败则递归地匹配aa,如果失败则递归地匹配b。

微软似乎在说他们的回溯速度比POSIX快,因为POSIX回溯将安排递归搜索,直到它确定它找到了最长的匹配。 Microsoft版本仍然回溯,但它没有进行额外的检查,直到确保他们找到了最长的匹配。 http://msdn.microsoft.com/en-us/library/dsy130b4%28v=vs.110%29.aspx中有一个例子。

没有回溯的正则表达式匹配器可以通过一次接受输入一个字符,并跟踪当时NFA中的哪些状态是有效的 - 可能有许多这样的状态。使用反向引用很难做到这一点,因为NFA的状态不能仅通过说明状态是否存在来描述。