DFA VS NFA
DFA:确定有限自动机。对一个输入,只有一个转移状态。
NFA:非确定有限自动机。对一个输入,可以有多个转移状态。
DFA的一个例子:
如果模式串为: abc|abcdef,待匹配串为abcdef,在DFA中,它总是会去尝试所有可能的匹配。
首先它会用abc中的a来匹配,发现满足;同时它还会用abcdef中的a来匹配,发现也满足。这时它会同时保留两个匹配的串。
接着往下匹配bc。
直到字符d。这时左边的abc无法匹配了,因而左边的模式就会被记录为不匹配。而右边的abcdef模式串仍然能够匹配。
这样就会一直用右边的abcdef模式串来匹配剩下的字符。
DFA总是试图去匹配所有的模式,并且通常是一种最长匹配。
而如果用NFA来匹配,这时就往往会采用一种最先匹配策略。
当abc串匹配了abcdef之后,它就认为匹配成功了,就不会再用右边的abcdef来匹配剩下的字符。
以下的python代码可以证实采用了NFA的匹配策略:
import re
p=re.compile(r'(abc)|(abcdef)')
s='abcdef'
m=p.match(s)
print m.groups()
会输出('abc', None)。
DFA的特点:没有回溯,速度非常快。但是不能捕获分组,因而支持的模式复杂度有限。
NFA的特点:功能强大,能支持极其复杂的模式。但是因为有回溯,速度视模式本身而定。不合理的模式会导致大量回溯,从而导致性能低下。
目前大多数正则表达式实现都是NFA,如python, java.util.regex, .NET等。部分古老的实现如awk实用DFA。
匹配优先 VS 忽略优先
匹配优先:*
忽略优先:*?
从路径中获取文件名:
两种路径方式: /usr/bin/gcc或者C:\Windows\Messenger,因此模式会涉及到两种,这里以前者为例。
有两种思路:
1. 从头开始匹配,这种思路本质上是希望将最后一个'/'前的字符全部去掉。用perl可写成如下:
$f =~ s{^.*/}{};
即用^.*/这个模式来匹配包括最后一个/在内的之前所有字符。
2. 从尾开始匹配,这种思路是希望从后往前找,直到碰到/就停止。
这时我们可以用以下的模式来匹配:
[^/]*$,然后使用分组捕获即可。
分析:无论哪一种思路,其实都有一个匹配优先,这就意味着都会有回溯。因而在NFA的情况下性能都不会特别好。