正则表达式转NFA

    最近一直在忙着写大作业,考试复习,复习算法的时候写了一些随笔,现在忙起来都落下了博客,这里有一个VC++写的大作业,主要是正则表达式转NFA并显示。内容如下。

数据结构描述

介绍一下NFA在表示的结构设计,由于NFA本身是一种有向图,所以这里的存储结构设计和邻接表相似,图中的每个节点后面是一些与其连接的节点的值,具体设计如图4.1。

 

图4.1

a)         Graph由若干个GraphLine组成,其中start和end标识了NFA的初始状态和终止状态的下标;

b)         GraphLine的由一个节点和以该节点为起点所指向的节点组成,而所指向的节点利用EdgeLink表示,其中有指向边上的转移字符和指向的节点组成;

c)         GraphNode表示图的基本组成节点,其中节点有唯一的ID和位置;

正则表达式转换NFA算法

基础的正则表达式,如图4.2所示

 

图4.2

对于正则表达式应用运算符部分构造方法如图4.3

 

图4.3

  1.     符号栈,即运算的符号,其存储的为wchar_t类型,为连接,左括号,选择3种运算符。
  2.     NFA,即保存的NFA,这里因为整个计算过程都是更新一个Graph结构,所以这里的NFA栈保留的其实是当前NFA的开始和结束信息,即start和end。

具体的主要算法执行流程:

  1.     遍历输入的正则表达式,这里正则表达式的保存在CString变量中,可以通过下标访问
  2.     首先初始化一张保存NFA的Graph结构,算法过程中的节点的数量不会超过正则表达式长度的2倍,所以这里直接开辟一个大小为正则表达式长度为2倍的Graph结构
  3.     遇到非运算符,及正则表达式里面的转移符号的时候,这里就需要构造一个基本的NFA, 一个初始状态,一个终止状态,然后由初始状态至终止状态有一条为该转移符号的边,此时仍然需要检查正则表达式的下一个符号,如果不是运算符或者为左括号,此时应该运算栈中添加一个连接运算符,然后将构造的基本NFA添加入NFA栈中,方便以后将基本的NFA进行其他选择,重复,连接运算
  4.     遇到非运算符时,需要分一下四种运算符的情况
    1.     如果是运算符“)”,即右括号,此符号属于运算级最高的符号了,所以它要在符号栈中弹出所有符号运算,直到遇到“)”匹配,运算过程中根据符号栈中弹出的符号计算
    2.     如果是运算符“(”,即左括号,此符号只是用来和右括号结合的,所以直接将该运算符压入符号栈中即可
    3.     如果是运算符“*”,即重复符号,这个在正则表达式中运算级最高,直接进行计算,计算方法就是从NFA栈中弹出一张图,然后得到两个未分配的新节点,添加4条上面图表示的那样的边,然后重新设定NFA的start和end之后将新的NFA压入NFA栈中即可,运算后检查其后跟随的元素,如果是转移符号或者左括号,则必须要向符号栈中添加连接符号
    4.     如果是运算符“+”,即选择符号,由于此符号的优先级没有连接符号高,所以此时应该弹出符号栈中优先级高于它的符号,但是“(”不参与弹出,所以这里只是弹出连接符号和自身“+”符号运算,然后将该符号压入符号栈等候计算
  5.     正则表达式遍历完毕之后,需要弹出所有的符号栈进行计算,最后NFA栈中的唯一NFA就是所求的NFA

接下来就是具体的运算的算法,这里点与点的连接通过更新Graph中相应的点的邻接链表即可

  1.     连接运算,此时需要弹出NFA栈中的两个NFA,然后将其中一个的end连接至另一个的start,然后更新新的NFA的start和end,压入NFA栈中。
  2.     选择运算,此时需要弹出NFA栈中的两个NFA,然后Graph重新分配两个节点,作为新的NFA的start和end,然后新的start分别连接弹出的两个NFA的start,弹出的两个NFA的end分别连接新的end即构成新的NFA,压入NFA栈中。
  3.     闭包运算,此时需要弹出NFA栈中的一个NFA,然后Graph重新分配两个节点,作为新的NFA的start和end,然后新的start连接弹出NFA的start,弹出NFA的end连接新的end,然后添加一条新的start到新的end的一条空边和一条旧的end到旧的start的一条空边,将新的NFA压入NFA栈中。

最终的运行Graph的结果输出样式如图4.4

 

图4.4

NFA的显示

NFA的显示是根据上面算法生成的Graph的结构进行显示显示结果如图4.5

 

 

具体的显示方法是从Graph的start节点开始调用绘制函数,该绘制函数的功能是首先检查该节点是否绘制,如果未绘制则进行绘制,如果已经绘制则不进行绘制,然后根据自己的位置确定他邻接点的位置,然后绘制至其他点的边,然后递归的调用其邻接点,函数的结束条件是绘制到end或者其邻接点已经绘制。

这里对代码类和函数的作用进行主要的说明,代码的实现细节可以参看代码中的注释

代码主要类视图如图5.1

 

图5.1

CInputExpDlg

正则表达式输入对话框

GraphNode

 

EdgeLink

 

GraphLine

 

Graph

 

TranslateModel

 

源代码下载地址:https://files.cnblogs.com/weixliu/RextoNFA.rar

转载请注明出处,谢谢,

 

posted @ 2013-01-06 16:43  weixliu  阅读(21024)  评论(3编辑  收藏  举报