将正则表达式转换为有限状态自动机

在前一文章中总结了一下对DFA和NFA两种有限状态自动机的认识,在实际应用中,例如开发编译器时,将正则表达式转换为自动机会非常重要的一环。如果对NFA和DFA不理解的可以看下这篇博文
看一下如何把正则表达式通过Thompson构造转换为NFA:
DFA和NFA理解
一个例子:
(and|any)转换为NFA“|”或者,也就是从开始分成两条路去选择。
将正则表达式转换为有限状态自动机_第1张图片
或者:

从初始状态分化两条对应字符为空字符的边,然后分别进入两个对应的状态机
将正则表达式转换为有限状态自动机_第2张图片
ε-NFA,在上篇文章中说到过,也是NFA,ε的意思是可以不读入字符就跳转到另一个状态。
Thompson 构造法

将正则表达式转换为NFA的算法是由贝尔实验室的Ken Thompson 给出的,这哥们跟丹尼斯.里奇共同开发了Unix, 而他开发了C语言的前身 B 语言。

他的算法如下:

最简单的正则表达式是单字符匹配,例如a 匹配输入字符”a”, 那么该表达式的NFA 构造如下:
将正则表达式转换为有限状态自动机_第3张图片

那么,两个这样的正则表达式合成的连接表达式ab 可以表示如下:
将正则表达式转换为有限状态自动机_第4张图片
实际上,它是先分别构造出两个表达式的NFA, 然后通过一条ℇ边,将两个NFA首尾连接起来。

下面我们看看,两个表达式进行 OR 操作的时候 | ,NFA怎么构造,构造图如下:
将正则表达式转换为有限状态自动机_第5张图片
要构造两个表达式的或操作: exp1 | exp2, 根据图示,首先分别构造两个表达式exp1 , exp2 各自的NFA: NFA1(上头虚线框), NFA2(下头虚线框), 然后再构造两个状态,初始状态(开头圆圈节点),和结束状态(末尾圆圈节点),初始状态延生处两条 ℇ 边,分别指向NFA1 和 NFA2 的开头,然后NFA1 和 NFA2的结尾各自延生出一条ℇ边,分别共同指向结束状态。

我们再看看 a | b 的NFA图:
将正则表达式转换为有限状态自动机_第6张图片
其原理跟前面所描述的是一样的。上头虚线框是表达式 a 的NFA, 下头虚线框是表达式 b 的NFA. 两个NFA的连接跟前面描述的一模一样

如果表达式是( (a|b) | cd) 呢,算法也同理,先构造 a | b 的NFA图,然后构造cd的NFA图。最后根据前面所说的办法,再将两个NFA连接起来:

上头大虚线框是 (a|d) 的NFA, 下头长匾虚线框是 cd的NFA. 然后首尾通过两个状态节点和ε边连接起来。
将正则表达式转换为有限状态自动机_第7张图片
大家可以看到, Thompson构造算法其实是一个自我递归的过程

我们再看看相应的闭包操作的构造过程:

exp*的NFA:

如果是自我从复0次,那直接从下面的边走到末尾节点。
将正则表达式转换为有限状态自动机_第8张图片
exp+(至少重复一次) 的NFA:
将正则表达式转换为有限状态自动机_第9张图片
exp?(重复0或1次)的NFA:
将正则表达式转换为有限状态自动机_第10张图片
任何复杂的正则表达式它的NFA的构造都是上面几种构造的组合, 例如表达式

(D*.D| D.D*)

构造算法如下:

  1. 构造 D 的NFA:
    将正则表达式转换为有限状态自动机_第11张图片

  2. 构造 D*:
    将正则表达式转换为有限状态自动机_第12张图片

  3. 构造 D*.D (由于.在正则表达式中是特殊字符,如果要仅仅想要表达它的符号内容,要在前面加上反斜杠做转义):

. 号的前部分是D*, 后部分是 D 的NFA.
将正则表达式转换为有限状态自动机_第13张图片
4. 构造 D.D*, 该表达式的NFA其实就是将上图 . 后面的部分挪到开头。
5. 根据OR 的构造法, 构造整个表达式 (D*.D | D.D*)的NFA:

上头是 D*.D 的NFA, 下头是 D.D*的NFA
将正则表达式转换为有限状态自动机_第14张图片

这就是一些基本的运用,复杂的正则表达式都是有基础的构造出来的,可以重复用上面的基础来构造出复杂的表达式。

转载自:http://blog.csdn.net/tyler_download/article/details/51072362

你可能感兴趣的:(深度包检测知识分类)