正規表示式轉NFA

2021-09-08 03:43:44 字數 3220 閱讀 5097

最近一直在忙著寫大作業,考試複習,複習演算法的時候寫了一些隨筆,現在忙起來都落下了部落格,這裡有乙個vc++寫的大作業,主要是正規表示式轉nfa並顯示。內容如下。

介紹一下nfa在表示的結構設計,由於nfa本身是一種有向圖,所以這裡的儲存結構設計和鄰接表相似,圖中的每個節點後面是一些與其連線的節點的值,具體設計如圖4.1。

圖4.1

a)         graph由若干個graphline組成,其中start和end標識了nfa的初始狀態和終止狀態的下標;

b)         graphline的由乙個節點和以該節點為起點所指向的節點組成,而所指向的節點利用edgelink表示,其中有指向邊上的轉移字元和指向的節點組成;

c)         graphnode表示圖的基本組成節點,其中節點有唯一的id和位置;

基礎的正規表示式,如圖4.2所示

圖4.2

對於正規表示式應用運算子部分構造方法如圖4.3

圖4.3

符號棧,即運算的符號,其儲存的為wchar_t型別,為連線,左括號,選擇3種運算子。

nfa,即儲存的nfa,這裡因為整個計算過程都是更新乙個graph結構,所以這裡的nfa棧保留的其實是當前nfa的開始和結束資訊,即start和end。

具體的主要演算法執行流程:

遍歷輸入的正規表示式,這裡正規表示式的儲存在cstring變數中,可以通過下標訪問

首先初始化一張儲存nfa的graph結構,演算法過程中的節點的數量不會超過正規表示式長度的2倍,所以這裡直接開闢乙個大小為正規表示式長度為2倍的graph結構

遇到非運算子,及正規表示式裡面的轉移符號的時候,這裡就需要構造乙個基本的nfa, 乙個初始狀態,乙個終止狀態,然後由初始狀態至終止狀態有一條為該轉移符號的邊,此時仍然需要檢查正規表示式的下乙個符號,如果不是運算子或者為左括號,此時應該運算棧中新增乙個連線運算子,然後將構造的基本nfa新增入nfa棧中,方便以後將基本的nfa進行其他選擇,重複,連線運算

遇到非運算子時,需要分一下四種運算子的情況

如果是運算子「)」,即右括號,此符號屬於運算級最高的符號了,所以它要在符號棧中彈出所有符號運算,直到遇到「)」匹配,運算過程中根據符號棧中彈出的符號計算

如果是運算子「(」,即左括號,此符號只是用來和右括號結合的,所以直接將該運算子壓入符號棧中即可

如果是運算子「*」,即重複符號,這個在正規表示式中運算級最高,直接進行計算,計算方法就是從nfa棧中彈出一張圖,然後得到兩個未分配的新節點,新增4條上面圖表示的那樣的邊,然後重新設定nfa的start和end之後將新的nfa壓入nfa棧中即可,運算後檢查其後跟隨的元素,如果是轉移符號或者左括號,則必須要向符號棧中新增連線符號

如果是運算子「+」,即選擇符號,由於此符號的優先順序沒有連線符號高,所以此時應該彈出符號棧中優先順序高於它的符號,但是「(」不參與彈出,所以這裡只是彈出連線符號和自身「+」符號運算,然後將該符號壓入符號棧等候計算

正規表示式遍歷完畢之後,需要彈出所有的符號棧進行計算,最後nfa棧中的唯一nfa就是所求的nfa

接下來就是具體的運算的演算法,這裡點與點的連線通過更新graph中相應的點的鄰接鍊錶即可

連線運算,此時需要彈出nfa棧中的兩個nfa,然後將其中乙個的end連線至另乙個的start,然後更新新的nfa的start和end,壓入nfa棧中。

選擇運算,此時需要彈出nfa棧中的兩個nfa,然後graph重新分配兩個節點,作為新的nfa的start和end,然後新的start分別連線彈出的兩個nfa的start,彈出的兩個nfa的end分別連線新的end即構成新的nfa,壓入nfa棧中。

閉包運算,此時需要彈出nfa棧中的乙個nfa,然後graph重新分配兩個節點,作為新的nfa的start和end,然後新的start連線彈出nfa的start,彈出nfa的end連線新的end,然後新增一條新的start到新的end的一條空邊和一條舊的end到舊的start的一條空邊,將新的nfa壓入nfa棧中。

最終的執行graph的結果輸出樣式如圖4.4

圖4.4

nfa的顯示是根據上面演算法生成的graph的結構進行顯示顯示結果如圖4.5

具體的顯示方法是從graph的start節點開始呼叫繪製函式,該繪製函式的功能是首先檢查該節點是否繪製,如果未繪製則進行繪製,如果已經繪製則不進行繪製,然後根據自己的位置確定他鄰接點的位置,然後繪製至其他點的邊,然後遞迴的呼叫其鄰接點,函式的結束條件是繪製到end或者其鄰接點已經繪製。

這裡對**類和函式的作用進行主要的說明,**的實現細節可以參看**中的注釋

**主要類檢視如圖5.1

圖5.1

cinputexpdlg

正規表示式輸入對話方塊

正規表示式轉NFA

正規表示式有三種基本的運算 連線 concatenation 例如 abc,由a,b,c組成 聯合 union 例如 a b c,表示a或者b或者c kleene閉包 kleene 例如 ab 表示ab串不出現,或者出現1次或一次以上 其它的運算如 等都可以用以上三種基本運算或者運算的組合來表示。2...

編譯原理 正規表示式轉NFA

從txt檔案中讀入正規表示式 include include include include include define max token 100 using namespace std 詞struct token int readtxt string filename,vector token...

正規表示式 轉

字元 描述 將下乙個字元標記為乙個特殊字元 或乙個原義字元 或乙個向後引用 或乙個八進位制轉義符。例如,n 匹配字元 n n 匹配乙個換行符。序列 匹配 而 則匹配 匹配輸入字串的開始位置。如果設定了regexp物件的multiline屬性,也匹配 n 或 r 之後的位置。匹配輸入字串的結束位置。如...