精通正则表达式五:NFA与DFA

定义

NFA与DFA是正则表达式引擎所使用的两种基本技术:

NFA:非确定型有穷自动机
DFA:确定型有穷自动机

作者用用电动机来比喻DFA,用汽油机来比喻NFA,他们确实有许多相似之处:
1.汽油机的历史更长,NFA的历史也比DFA长
2.汽油机应用更加广泛,NFA的应用也比DFA更加广泛
3.汽油机可分为达到加利福尼亚州的尾气排放标准(更严格)和没有达到两种,NFA也分为POSIX NFA和传统型NFA,POSIX NFA是为了规范正在表达式的使用,也是一种新标准。
因此,正则表达式的引擎实际上可以分为三种:

DFA
传统型NFA
POSIX NFA

还有一类特殊的的引擎类型,就是DFA与NFA混合,如:
GNU grep/egrep、Tcl、GUN awk。
我们所使用的许多语言都是用的传统型NFA,比如Java、Perl、Python、PHP、Ruby等。

测试引擎类型

有时候并不知道我们使用的工具是那种类型,可以用几个测试用的表达式来分辨引擎的类型
1.看是否支持忽略优先量词,如果支持,基本就能确定是传统型NFA:

用正则表达式:nfa|nfa not 来匹配字符串 nfa not,如果匹配结果是nfa,就是传统型NFA

2.如果第一步不匹配,再试捕获型括号,DFA不支持捕获型括号和回溯,当然,也可能会遇到同时使用两种引擎的混合系统,在这种系统中,如果没有使用捕获型括号,就会使用DFA,反之使用NFA

表达式主导与文本主导

DFA和NFA反映了正则表达式在应用算法上的根本差异,NFA为表达式主导,DFA为文本主导,有什么区别呢?

表达式主导:
如用正则表达式:to(nite|knite|night) 来匹配文本:tonight,正则表达式中第一个是‘t’,他会重复尝试,知道在匹配文本中找到‘t’为止,之后检查后续文本是否匹配‘o’,如果能,会接着检查选择分支,引擎会依次尝试‘nite’,‘knite’,‘night’这三种可能,直到匹配成功或全部尝试完后匹配失败,在匹配‘nite’时,会想上面一样一次匹配‘n’,‘i’,‘t’,‘e’,如果失败,在尝试另一种可能。表达式中的控制权在不同的元素之间转换,所以称之为“表达式主导”。

文本主导:
DFA引擎在扫描字符串时,会记录当前有效的所有可能匹配,如上例中,当扫描到匹配文本的‘i’字符时,它会记录下两个可能匹配的位置‘nite’中的‘i’和‘night’中的‘ni’,从而把knight淘汰,再扫描匹配文本中的‘g’,这时只有‘night’能匹配,只剩下一种可能,然后依次匹配‘h’和‘t’,引擎发现匹配已经完成,报告成功。它是扫描的字符串中的每个字符都对引擎进行了控制,所以称为“文本主导”。

我们会发现,NFA效率会低于DFA,因为DFA每个字符只会扫描一次,而NFA对同一字符会扫描多次。

你可能感兴趣的:(正则表达式)