从头开始,搭建一个正则表达式引擎(一)整体构架、预处理
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
大概和我不以程序員為職業(yè)有關(guān)吧,我本人是比較喜歡算法的那種,當(dāng)然是比不了科班出身的。
比如我就寫(xiě)過(guò)很多版本的算術(shù)表達(dá)式解析器,優(yōu)先級(jí)堆棧的;二叉樹(shù)的;分治策略的;修正計(jì)算順序的……
正則表達(dá)式兩個(gè)我也寫(xiě)過(guò)兩個(gè),一個(gè)采用的回溯算法,另一個(gè)則是二叉樹(shù)版本的,雖然這倆倒是都能用吧,但是對(duì)分組的處理(分組是采用遞歸調(diào)用的方法實(shí)現(xiàn)的,相當(dāng)于另一個(gè)正則表達(dá)式)實(shí)在是不太好,這個(gè)缺點(diǎn)導(dǎo)致很多時(shí)候搜索的結(jié)果會(huì)遺漏很多,而二叉樹(shù)版本的還有個(gè)缺點(diǎn)是選擇操作“|”只能獲得第一個(gè)匹配…………
就效果看,這倆都是殘次品。
后來(lái),我在vczh的博客里看到了他的一個(gè)科普貼子,在那里面,我才了解到還有有限自動(dòng)機(jī)這種方法。
之后我花了不少時(shí)間去思考,因?yàn)榭傆X(jué)得這個(gè)方法太麻煩了,斷斷續(xù)續(xù)有2-3個(gè)月吧,想來(lái)想去也沒(méi)想出什么更好的法子,最后還是覺(jué)得只有用有限自動(dòng)機(jī)才合適。
不過(guò)多少我還是做了些修改的,倒不是為了效率,是為了減少打字量……
主要修改如下:
(1)不每個(gè)字符轉(zhuǎn)移一次狀態(tài),而是盡可能的把連續(xù)的字符作為一個(gè)整體進(jìn)行比對(duì)。
(2)不弄什么字母分組表,數(shù)據(jù)結(jié)構(gòu)就使用指針鏈表,節(jié)省工作量
(3)“{a,b}”這種有次數(shù)限定的重復(fù),vczh是采用復(fù)制節(jié)點(diǎn)實(shí)現(xiàn)的,我采用增加狀態(tài)邊來(lái)解決(效率會(huì)下降)
(4)分組命名,向前向后預(yù)查這些花哨而實(shí)用的功能,俺就丟了
那么,整體上看,正則表達(dá)式引擎的設(shè)計(jì)我就劃分為如下幾個(gè)步驟:
1)對(duì)規(guī)則字符串做預(yù)處理,主要是換行這類(lèi)要使用轉(zhuǎn)義符的字符,這個(gè)預(yù)處理可以把這些字符還原
2)將規(guī)則字符串進(jìn)行劃分,修正成操作符、操作數(shù)的列表形式
3)對(duì)這個(gè)列表進(jìn)行處理,獲得NFA狀態(tài)轉(zhuǎn)移圖
4)將NFA狀態(tài)轉(zhuǎn)移圖里的ε邊消去,減少狀態(tài)轉(zhuǎn)移數(shù)
5)利用得到的狀態(tài)轉(zhuǎn)移圖,對(duì)字符串進(jìn)行匹配
第一步很容易解決,無(wú)非就是找到'\',然后將后跟't'、'n'、'r'等字符的修正為相應(yīng)的制表、換行……
第二步也相對(duì)簡(jiǎn)單,無(wú)非是識(shí)別幾個(gè)操作符:
'*'、'+'、'?'、'*?'、'+?'、'??'、'('、'(:'、')'、'|'、'['、']'、'{'、'}'
比較特殊的是:
因?yàn)榘堰B續(xù)的字符看做一個(gè)整體,所以在后跟重復(fù)或者可選運(yùn)算符時(shí),要檢查一下是否需要斷開(kāi)字符串;
'['后面要一直讀取到前面不是'\'的']',期間不作分析,結(jié)果作為一個(gè)整體
'{'后面有'{a}'、'{a,}'、'{,a}'、'{a,b}'這幾種形式,同樣是讀取為一個(gè)整體
還有一個(gè)隱藏的操作符,它的作用是將兩個(gè)操作數(shù)連接起來(lái),類(lèi)似算術(shù)里的“*”,關(guān)于它另有介紹[*]
如此一來(lái),列表里的成員記錄的內(nèi)容就會(huì)有好幾種,python里這不算啥,C++里就麻煩一些,我采用的是struct和union來(lái)回嵌套+標(biāo)志字節(jié)的辦法來(lái)處理這個(gè)問(wèn)題。
(如果使用正則表達(dá)式這一步非常輕松,可是如果用了就真成了笑話(huà)了)
第三步見(jiàn)下一帖
[*]被隱藏了的“連接”運(yùn)算符,比如“ab[cd]”這個(gè)字符串,分析后的兩個(gè)元素“ab”和“[cd]”正是通過(guò)這個(gè)操作符連接的。這個(gè)隱藏運(yùn)算符我是用后面的Operator類(lèi)的一個(gè)補(bǔ)足函數(shù)來(lái)填充修復(fù)的,不然就得另外寫(xiě)一個(gè)函數(shù)了。
轉(zhuǎn)載于:https://my.oschina.net/liudiwu/blog/111327
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專(zhuān)家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的从头开始,搭建一个正则表达式引擎(一)整体构架、预处理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: cocos2d-x按钮CCControl
- 下一篇: burpsuite 简单介绍