大概和我不以程序員爲職業有關吧,我本人是比較喜歡算法的那種,固然是比不了科班出身的。 html
好比我就寫過不少版本的算術表達式解析器,優先級堆棧的;二叉樹的;分治策略的;修正計算順序的…… python
正則表達式兩個我也寫過兩個,一個採用的回溯算法,另外一個則是二叉樹版本的,雖然這倆卻是都能用吧,可是對分組的處理(分組是採用遞歸調用的方法實現的,至關於另外一個正則表達式)實在是不太好,這個缺點致使不少時候搜索的結果會遺漏不少,而二叉樹版本的還有個缺點是選擇操做「|」只能得到第一個匹配………… 程序員
就效果看,這倆都是殘次品。 正則表達式
後來,我在vczh的博客裏看到了他的一個科普貼子,在那裏面,我才瞭解到還有有限自動機這種方法。 算法
以後我花了很多時間去思考,由於總以爲這個方法太麻煩了,斷斷續續有2-3個月吧,想來想去也沒想出什麼更好的法子,最後仍是以爲只有用有限自動機才合適。 數據結構
不過多少我仍是作了些修改的,倒不是爲了效率,是爲了減小打字量…… 函數
主要修改以下: spa
(1)不每一個字符轉移一次狀態,而是儘量的把連續的字符做爲一個總體進行比對。 設計
(2)不弄什麼字母分組表,數據結構就使用指針鏈表,節省工做量 指針
(3)「{a,b}」這種有次數限定的重複,vczh是採用複製節點實現的,我採用增長狀態邊來解決(效率會降低)
(4)分組命名,向前向後預查這些花哨而實用的功能,俺就丟了
那麼,總體上看,正則表達式引擎的設計我就劃分爲以下幾個步驟:
1)對規則字符串作預處理,主要是換行這類要使用轉義符的字符,這個預處理能夠把這些字符還原
2)將規則字符串進行劃分,修正成操做符、操做數的列表形式
3)對這個列表進行處理,得到NFA狀態轉移圖
4)將NFA狀態轉移圖裏的ε邊消去,減小狀態轉移數
5)利用獲得的狀態轉移圖,對字符串進行匹配
第一步很容易解決,無非就是找到'\',而後將後跟't'、'n'、'r'等字符的修正爲相應的製表、換行……
第二步也相對簡單,無非是識別幾個操做符:
'*'、'+'、'?'、'*?'、'+?'、'??'、'('、'(:'、')'、'|'、'['、']'、'{'、'}'
比較特殊的是:
由於把連續的字符看作一個總體,因此在後跟重複或者可選運算符時,要檢查一下是否須要斷開字符串;
'['後面要一直讀取到前面不是'\'的']',期間不做分析,結果做爲一個總體
'{'後面有'{a}'、'{a,}'、'{,a}'、'{a,b}'這幾種形式,一樣是讀取爲一個總體
還有一個隱藏的操做符,它的做用是將兩個操做數鏈接起來,相似算術裏的「*」,關於它另有介紹[*]
如此一來,列表裏的成員記錄的內容就會有好幾種,python裏這不算啥,C++裏就麻煩一些,我採用的是struct和union來回嵌套+標誌字節的辦法來處理這個問題。
(若是使用正則表達式這一步很是輕鬆,但是若是用了就真成了笑話了)
第三步見下一帖
[*]被隱藏了的「鏈接」運算符,好比「ab[cd]」這個字符串,分析後的兩個元素「ab」和「[cd]」正是經過這個操做符鏈接的。這個隱藏運算符我是用後面的Operator類的一個補足函數來填充修復的,否則就得另外寫一個函數了。