深入入門正則表達式(java) - 匹配原理 - 1 - 引擎分類與普適原則

深入入門正則表達式(java) - 引言

深入入門正則表達式(java) - 1 - 入門基礎 
深入入門正則表達式(java) - 2 - 基本實例

深入入門正則表達式(java) - 3 - 正則在java中的使用

深入入門正則表達式(java) - 匹配原理 - 1 - 引擎分類與普適原則

深入入門正則表達式(java) - 匹配原理 - 2 - 回溯

 

 

本節第一部分主要介紹正則引擎的分類,由於java屬於NFA,所以只重點介紹此類。其餘類型簡要或不做介紹。

分類的內容全部來自《精通正則表達式》v3

 

引擎類型程序
DFAawk(大多數版本)、egrep(大多數版本)、flex、lex、MySQL、Procmail
傳統NFAGNU Emacs、Java、grep(大多數版本)、less、more、.NET語言、PCRE library、Perl、PHP(所有三套正則庫)、Python、Ruby、sed(大多數版本)、vi
POSIX NFAmawk、Mortice Kern Systems'utilities、GNU Emacs(明確指定時使用)
DFA/NFA混合GNU awk、GNU grep/egrep、Tcl

 

NFA(非確定型有窮自動機):表達式主導

正則:“to(nite|knight|night)”

目標文本:“tonight”

正則表達式從“t”開始,每次檢查一部分(由引擎查看表達式的一部分),同時檢查當前文本是否匹配表達式的當前部分。如果是,則繼續表達式的下一部分,直到表達式的所有部分都能匹配。

此例中第一個元素是“t”,它會重複嘗試,在目標字符串中找到“t”爲止,然後檢查“o”,過程與此一致。然後是“(nite|knight|night)”部分,表達式會一次嘗試,直到宣告匹配成功或失敗纔會停止。 表達式中的控制權在不同元素之間轉換,所以作者稱其爲“表達式主導”

所以正則:“nfa|nfa not”,目標字符串:“nfa not”中,也只是匹配“nfa”而已,而不會完整的匹配。

 

DFA (確定型有窮自動機) :文本主導

DFA引擎在掃描字符串時,會記錄“當前有效”的所有匹配可能。

還是最初的例子,引擎移動到“t”時,它會在當前處理匹配可能中添加一個潛在的可能

接下來掃描的每個字符,都會更新當前的可能匹配序列。繼續掃描兩個字符之後的情況如上圖。分支“knight”被排除。

書中作者稱其問文本主導,是因爲掃描每個字符的時候都對引擎進行了控制

 

 

測試引擎類型

1.如果支持忽略優先量詞,那麼基本就是傳統NFA。DFA不支持忽略優先量詞,POSIX NFA中也沒有意義。

2.DFA不支持捕獲型括號和回溯。在這兩種混合類型的引擎中,如果沒有使用捕獲型括號,就會使用DFA

ps:在RegexBuddy中似乎只有傳統NFA,起碼做1的驗證時結果是這樣的,所以DFA和混合型引擎在這就不做驗證了,本文也主要針對java,所以這裏指着重介紹和java相關內容

 

 

 

兩條普適原則(來自 《精通正則表達式》 v3) :

1.優先匹配最左面(最靠開頭)的匹配結果

注意:此原則並沒有規定優先匹配結果的長度,而只是規定在所有可能的匹配結果中,優先選擇最左邊的(可能有)。

作者關於此原則的解釋:匹配先從需要查找的字符串的起始位置嘗試匹配。這裏的“嘗試匹配”的意思是:在當前位置測試整個正則表達式能匹配的每個可 能。如果在當前位置測試了所有的可能之後找不到匹配結果,就需要從字符的第二個字符之前的位置開始重新嘗試……只有在嘗試過所有的起始位置(直到字符串的 最後一個字符)都找不到匹配結果的情況下,纔會報告失敗。

下面給出一個例子:

目標字符串“This is a cat.”

我想匹配字符“is”,我的正則爲“is”

結果如下(圖1):

這裏找到了兩個結果,根據原則1,最先找到的是“this”中的“is”,而並沒有找到“is”這個單詞。這也很容易理解。下面我們看看RegexBuddy中debug的過程

這裏怎麼會有這麼多字符,目標字符串實際只有13個字符,那麼多出來的那些都是哪來的呢?

我覺得,RegexBuddy是把字符與字符之間的位置也算爲一個character。再來看看圖1

我之所以把每一個字符都裝在表格裏,就是讓大家看的清楚。這裏,每一個豎線(其實是不存在的)也作爲一個character,我覺得這樣是有道理的,比如零寬斷言,它的匹配就是在某一個豎線的位置。我們不妨用“^”測試一下,看看debug的結果。


 

當正則以“字符串起始位置錨點”開頭,引擎就會知道如果能匹配,那麼肯定是從字符串開頭,所以不需要做更多的嘗試。

ps:這和RegexBuddy上面的debug結果似乎是矛盾的。確實是這樣,不知道是不是其一個bug,起碼v3.5.4是這樣的

RegexBuddy暫時是不支持字符組的集合操作的,不知這算功能缺失還是算個bug

 

 

2.標準的量詞(*,+,?,{m,n})是匹配優先的

目標字符串:“copyright 2003”

正則:“.*”,那麼匹配的結果爲全部字符

正則:“.*[0-9]*”,這個時候,由於量詞是匹配優先的,所以“.*”會匹配整個字符串,而後面的“[0-9]*”怎什麼也匹配不到 ,這並不影響最終結果,因爲“*”表示0個也可以,我們可以添加一組括號來驗證這個結果,如下圖顯示的一樣

我們現在將正則改成這樣:“.*[0-9]+”,這時候“.*”也會先把字符串全部匹配,之後是“[0-9]+”這個部分,發現它要求至少匹配到一 個數字才行,所以它會強迫“.*”吐出它之前匹配到的內容給自己使用,當“.*”吐出字符“3”之後,“[0-9]+”成功匹配,至此匹配結束,不再進行 其他嘗試。

我們來debug看看這一過程:

書中作者總結爲:先來先服務。我覺得,也就是說,多個匹配優先量詞時,如果目標字符串“不能無法同時滿足其需求”,那麼寫在前面的量詞會得到儘量多的字符,後面的量詞會像“類似”忽略優先量詞一樣進行匹配 - 給點就行。

 

 

 

 

轉貼請保留以下鏈接

本人blog地址

http://su1216.iteye.com/

http://blog.csdn.net/su1216/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章