DFA和NFA

1.歷史:正則表達式

引用算法


正則表達式萌芽於1940年代的神經生理學研究,由著名數學家Stephen Kleene第一個正式描述。具體地說,Kleene概括了前述的神經生理學研究,在一篇題爲《正則集代數》的論文中定義了「正則集」,並在其上定義了一個代數系統,而且引入了一種記號系統來描述正則集,這種記號系統被他稱爲「正則表達式」。在理論數學的圈子裏被研究了幾十年以後,1968年,後來發明了UNIX系統的Ken Thompson第一個把正則表達式用於計算機領域,開發了qed和grep兩個實用文本處理工具,取得了巨大成功。在此後十幾年裏,一大批一流計算機科學家和黑客對正則表達式進行了密集的研究和實踐。在1980年代早期,UNIX運動的兩個中心貝爾實驗室和加州大學伯克利分校分別圍繞grep工具對正則表達式引擎進行了研究和實現。與之同時,編譯器「龍書」的做者Alfred Aho開發了Egrep工具,大大擴展和加強了正則表達式的功能。此後,他又與《C程序設計語言》的做者Brian Kernighan等三人一塊兒發明了流行的awk文本編輯語言。到了1986年,正則表達式迎來了一次飛躍。先是C語言頂級黑客Henry Spencer以源代碼形式發佈了一個用C語言寫成的正則表達式程序庫(當時還不叫open source),從而把正則表達式的奧妙帶入尋常百姓家,而後是技術怪傑Larry Wall橫空出世,發佈了Perl語言的第一個版本。自那之後,Perl一直是正則表達式的旗手,能夠說,今天正則表達式的標準和地位是由Perl塑造的。Perl 5.x發佈之後,正則表達式進入了穩定成熟期,其強大能力已經征服了幾乎全部主流語言平臺,成爲每一個專業開發者都必須掌握的基本工具。less



2.DFA和NFA工具

引用性能

理解DFA和NFA
正則表達式引擎分紅兩類,一類稱爲DFA(肯定性有窮自動機),另外一類稱爲NFA(非肯定性有窮自動機)。兩類引擎要順利工做,都必須有一個正則式和一個文本串,一個捏在手裏,一個吃下去。DFA捏着文本串去比較正則式,看到一個子正則式,就把可能的匹配串全標註出來,而後再看正則式的下一個部分,根據新的匹配結果更新標註。而NFA是捏着正則式去比文本,吃掉一個字符,就把它跟正則式比較,匹配就記下來:「某年某月某日在某處匹配上了!」,而後接着往下幹。一旦不匹配,就把剛吃的這個字符吐出來,一個個的吐,直到回到上一次匹配的地方。
DFA與NFA機制上的不一樣帶來5個影響:
1. DFA對於文本串裏的每個字符只需掃描一次,比較快,但特性較少;NFA要翻來覆去吃字符、吐字符,速度慢,可是特性豐富,因此反而應用普遍,當今主要的正則表達式引擎,如Perl、Ruby、Python的re模塊、Java和.NET的regex庫,都是NFA的。
2. 只有NFA才支持lazy和backreference等特性;
3. NFA急於邀功請賞,因此最左子正則式優先匹配成功,所以偶爾會錯過最佳匹配結果;DFA則是「最長的左子正則式優先匹配成功」。
4. NFA缺省採用greedy量詞(見item 4);
5. NFA可能會陷入遞歸調用的陷阱而表現得性能極差。

我這裏舉一個例子來講明第3個影響。

例如用正則式/perl|perlman/來匹配文本 ‘perlman book’。若是是NFA,則以正則式爲導向,手裏捏着正則式,眼睛看着文本,一個字符一個字符的吃,吃完 ‘perl’ 之後,跟第一個子正則式/perl/已經匹配上了,因而記錄在案,往下再看,吃進一個 ‘m’,這下糟了,跟子式/perl/不匹配了,因而把m吐出來,向上彙報說成功匹配 ‘perl’,再也不關心其餘,也不嘗試後面那個子正則式/perlman/,天然也就看不到那個更好的答案了。

若是是DFA,它是以文本爲導向,手裏捏着文本,眼睛看着正則式,一口一口的吃。吃到/p/,就在手裏的 ‘p’ 上打一個鉤,記上一筆,說這個字符已經匹配上了,而後往下吃。當看到 /perl/ 以後,DFA不會停,會嘗試再吃一口。這時候,第一個子正則式已經山窮水盡了,沒得吃了,因而就甩掉它,去吃第二個子正則式的/m/。這一吃好了,由於又匹配上了,因而接着往下吃。直到把正則式吃完,心滿意足往上報告說成功匹配了 ‘perlman’。

由此可知,要讓NFA正確工做,應該使用 /perlman|perl/ 模式。

經過以上例子,能夠理解爲何NFA是最左子式匹配,而DFA是最長左子式匹配。實際上,若是仔細分析,關於NFA和DFA的不一樣之處,均可以找出道理。而明白這些道理,對於有效應用正則表達式是很是有意義的。flex

 

寫道設計

正則表達式的形式定義故意很是精簡,避免定義多餘的量詞 ? 和 +,它們能夠被表達爲: a+ = aa* 和 a? = (a|ε)。有時增長補算子 ~ ;~R 指示在 Σ* 上的不在 R 中的全部字符串的集合。補算子是多餘的,由於它使用其餘算子來表達(儘管計算這種表示的過程是複雜的,而結果可能指數性的增大)。
這種意義上的正則表達式能夠表達正則語言,精確的是可被有限狀態自動機接受的語言類。可是在簡潔性上有重要區別。某類正則語言只能用大小指數增加的自動機來描述,而要求的正則表達式的長度只線性的增加。正則表達式對應於喬姆斯基層級的類型-3文法。在另外一方面,在正則表達式和不致使這種大小上的爆炸的非肯定有限狀態自動機(NFA)之間有簡單的映射;爲此 NFA 常常被用做正則表達式的替表明示。
咱們還要在這種形式化中研究表達力。以下面例子所展現的,不一樣的正則表達式能夠表達一樣的語言: 這種形式化中存在着冗餘。
有可能對兩個給定正則表達式寫一個算法來斷定它們所描述的語言是否本質上相等,簡約每一個表達式到極小肯定有限自動機,肯定它們是否同構(等價)。
這種冗餘能夠消減到什麼程度? 咱們能夠找到仍有徹底表達力的正則表達式的有趣的子集嗎? Kleene 星號和並集明顯是須要的,可是咱們或許能夠限制它們的使用。這提出了一個使人驚奇的困難問題。由於正則表達式如此簡單,沒有辦法在語法上把它重寫成某種規範形式。過去公理化的缺少致使了星號高度問題。最近 Dexter Kozen 用克萊尼代數公理化了正則表達式。
不少現實世界的「正則表達式」引擎實現了不能用正則表達式代數表達的特徵。遞歸

目前正則引擎支持的語言種類:開發

 

引擎類型 程序
DFA awk(大多數版本)、egrep(大多數版本)、flex、lex、MySQL、Procmail
傳統型 NFA GNU Emacs、Java、grep(大多數版本)、less、more、.NET語言、PCRE library、Perl、PHP(全部三套正則庫)、Python、Ruby、set(大多數版本)、vi
POSIX NFA mawk、Mortice Lern System's utilities、GUN Emacs(明確指定時使用)
DFA/NFA混合 GNU awk、 GNU grep/egrep、 Tcl

 

引伸閱讀:字符串

爲何輸入這種正則會致使假死?http://rdc.taobao.com/team/jm/archives/2432

相關文章
相關標籤/搜索