正則表達式轉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

 

源代碼下載地址:http://files.cnblogs.com/weixliu/RextoNFA.rar

轉載請注明出處,謝謝,

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM