b|.a|abc
开始,应用输入a
将得到模式a|bc
。应用输入b
将得到模式c
。此时,模式将在下一个字符是c
时匹配。 - conartist6这是一个简单的、非技术性的解释,摘自Jeffrey Friedl的书《精通正则表达式》。
注意:
虽然这本书通常被认为是“正则表达式圣经”,但似乎有一些争议,即在DFA和NFA之间所做的区分是否正确。我不是计算机科学家,也不理解真正的“正则”表达式背后的大部分理论,无论是确定性还是不确定性的。在争议开始后,我删除了这个答案,但自那以后它已经被引用到其他答案的评论中。我非常感兴趣进一步讨论这个问题——Friedl真的错了吗?或者我理解Friedl的意思错了吗(但我昨晚重新阅读了那一章,就像我记得的那样...)?
编辑:看来Friedl和我确实错了。请查看Eamon下面的优秀评论。
原始答案:
一个DFA引擎逐个字符地扫描输入字符串,并尝试(并记住)正则表达式在此处可能匹配的所有可能方式。如果它到达字符串的末尾,则声明成功。
想象一下字符串AAB
和正则表达式A*AB
。现在我们逐个字母地遍历我们的字符串。
A
:
A*
匹配。A*
(允许零次重复)并使用正则表达式中的第二个 A
进行匹配。A
:
A*
进行匹配。B
进行匹配。第二分支失败了。但是:A*
并使用第二个 A
进行匹配。B
:
A*
或继续在正则表达式中移动到下一个标记 A
进行匹配。第一分支失败了。DFA 引擎从不在字符串中回溯。
A*
:匹配AA
。记住回溯位置0(字符串的开头)和1。A
:不匹配。但是我们有一个可以返回并重试的回溯位置。正则表达式引擎向后移动一个字符。现在A
匹配。B
:匹配。到达正则表达式的末尾(还剩一个回溯位置)。太好了!NFA和DFA都是有限自动机,正如它们的名称所示。
两者都可以表示为一个起始状态、一个成功(或“接受”)状态(或一组成功状态)和一个列出转换的状态表。
在DFA的状态表中,每个<state₀, input>
键将转移到一个且仅一个state₁
。
在NFA的状态表中,每个<state₀, input>
将转移到一组状态。
当你拿到一个DFA,将其重置为起始状态,给它一系列输入符号,你将准确地知道它处于哪个结束状态以及它是否是一个成功状态。
然而,当你拿到一个NFA时,对于每个输入符号,它将查找可能结果状态的集合,并(理论上)随机地、“非确定性地”选择其中一个。如果存在一系列随机选择导致该输入字符串的某个成功状态,则称该NFA成功。换句话说,你需要假装它总是能够正确地选择。
计算机学科早期的一个问题是,由于其神奇性质,NFAs是否比DFAs更强大,最终的答案是不是,因为任何NFA都可以被转化成等价的DFA。 它们的能力和限制完全相同。
对于那些想知道真实的、非神奇的NFA引擎如何“神奇地”为给定符号选择正确的后继状态的人,this page介绍了两种常见的方法。
我认为Jan Goyvaerts在《正则表达式,完整教程》一书中给出的解释最实用。请参阅PDF文件的第7页:
https://www.princeton.edu/~mlovett/reference/Regular-Expressions.pdf
在第7页提到的其他要点中,有两种正则表达式引擎:文本导向引擎和正则表达式导向引擎。杰弗里·弗里德尔称它们分别为DFA和NFA引擎。...某些非常有用的功能,例如惰性量词和反向引用,只能在正则表达式导向引擎中实现。