1. 程式人生 > >vb.net正則表示式快速入門(3)完

vb.net正則表示式快速入門(3)完

作者:lzmtw

10.選擇符

正則表示式中“ ¦”表示選擇。

你可以用選擇符匹配多個可能的正則表示式中的一個。如果你想搜尋文字“cat”或“dog”,你可以用 < <cat¦dog> > 。

如果你想有更多的選擇,你只要擴充套件列表 < <cat¦dog¦mouse¦fish> > 。

選擇符在正則表示式中具有最低的優先順序,也就是說,它告訴引擎要麼匹配選擇符左邊的所有表示式,要麼匹配右邊的所有表示式。

你也可以用圓括號來限制選擇符的作用範圍。如 < <\b(cat¦dog)\b> > ,這樣告訴正則引擎把(cat ¦dog)當成一個正則表示式單位來處理。


注意正則引擎的“急於表功”性

正則引擎是急切的,當它找到一個有效的匹配時,它會停止搜尋。因此在一定條件下,選擇符兩邊的表示式的順序對結果會有影響。

假設你想用正則表示式搜尋一個程式語言的函式列表:Get,GetValue,Set或SetValue。一個明顯的解決方案是

          < <Get¦GetValue¦Set¦SetValue> > 。

讓我們看看當搜尋SetValue時的結果。因為 < <Get> > 和 < <GetValue> > 都失敗了,而 < <Set> > 匹配成功。

因為正則導向的引擎都是“急切”的,所以它會返回第一個成功的匹配,就是“Set”,而不去繼續搜尋是否有其他更好的匹配。

和我們期望的相反,正則表示式並沒有匹配整個字串。

有幾種可能的解決辦法。

    一是考慮到正則引擎的“急切”性,改變選項的順序,例如我們使用

             < <GetValue¦Get¦SetValue¦Set> > ,

             這樣我們就可以優先搜尋最長的匹配。

    我們也可以把四個選項結合起來成兩個選項:

             < <Get(Value)?¦Set(Value)?> > 。

             因為問號重複符是貪婪的,所以SetValue總會在Set之前被匹配。

     一個更好的方案是使用單詞邊界:

             < <\b(Get¦GetValue¦Set¦SetValue)\b> > 或 < <\b(Get(Value)?¦Set(Value)?\b> > 。

            更進一步,既然所有的選擇都有相同的結尾,我們可以把正則表示式優化為 < <\b(Get¦Set)(Value)?\b> > 。

11.組與向後引用

把正則表示式的一部分放在圓括號內,你可以將它們形成組。然後你可以對整個組使用一些正則操作,例如重複操作符。

要注意的是,只有圓括號“()”才能用於形成組。“[]”用於定義字符集。“{}”用於定義重複操作。

當用“()”定義了一個正則表示式組後,正則引擎則會把被匹配的組按照順序編號,存入快取

當對被匹配的組進行向後引用的時候,可以用“\數字”的方式進行引用。

        < <\1> > 引用第一個匹配的後向引用組,

        < <\2> > 引用第二個組,以此類推,

        < <\n> > 引用第n個組。

    而 < <\0> > 則引用整個被匹配的正則表示式本身

我們看一個例子。假設你想匹配一個HTML標籤的開始標籤和結束標籤,以及標籤中間的文字。比如

         <B>This is a test</B> ,

我們要匹配 <B> 和 </B> 以及中間的文字。我們可以用如下正則表示式:

       “ <([A-Z][A-Z0-9]*)[^> ]*> .*? </\1> ”

首先,“ <”將會匹配“ <B> ”的第一個字元“ <”。

然後[A-Z]匹配B,[A-Z0-9]*將會匹配0到多次字母數字,後面緊接著0到多個非“> ”的字元。

最後正則表示式的“> ”將會匹配“ <B> ”的“> ”。

接下來正則引擎將對結束標籤之前的字元進行惰性匹配,直到遇到一個“ </”符號。

然後正則表示式中的“\1”表示對前面匹配的組“([A-Z][A-Z0-9]*)”進行引用,在本例中,被引用的是標籤名“B”

所以需要被匹配的結尾標籤為“ </B> ”你可以對相同的後向引用組進行多次引用, 

        < <([a-c])x\1x\1> >

將匹配“axaxa”、“bxbxb”以及“cxcxc”。如果用數字形式引用的組沒有有效的匹配,則引用到的內容簡單的為空。

一個後向引用不能用於它自身

              < <([abc]\1)> > 是錯誤的。

因此你不能將 < <\0> > 用於一個正則表示式匹配本身,它只能用於替換操作中。

後向引用不能用於字符集內部

           < <(a)[\1b]> > 中的 < <\1> > 並不表示後向引用。在字符集內部, < <\1> > 可以被解釋為八進位制形式的轉碼。

向後引用會降低引擎的速度,因為它需要儲存匹配的組。如果你不需要向後引用,你可以告訴引擎對某個組不儲存。例如:

           < <Get(?:Value)> > 。其中“(”後面緊跟的“?:”會告訴引擎對於組(Value),不儲存匹配的值以供後向引用。


重複操作與後向引用

當對組使用重複操作符時,快取裡後向引用內容會被不斷重新整理,只保留最後匹配的內容。例如:

          < <([abc] )=\1> > 將匹配“cab=cab”,但是

          < <([abc]) =\1> > 卻不會。

因為([abc])第一次匹配“c”時,“\1”代表“c”;然後([abc])會繼續匹配“a”和“b”。最後“\1”代表“b”,所以它會匹配“cab=b”。

應用:檢查重複單詞--當編輯文字時,很容易就會輸入重複單詞,例如

         “the the”。

使用 < <\b(\w )\s \1\b> > 可以檢測到這些重複單詞。要刪除第二個單詞,只要簡單的利用替換功能替換掉“\1”就可以了。


組的命名和引用

在PHP,Python中,可以用 < <(?P <name> group)> > 來對組進行命名。

在本例中,詞法?P <name> 就是對組(group)進行了命名。其中name是你對組的起的名字。你可以用(?P=name)進行引用。

.NET的命名組.NETframework也支援命名組。不幸的是,微軟的程式設計師們決定發明他們自己的語法,而不是沿用Perl、Python的規則

目前為止,還沒有任何其他的正則表示式實現支援微軟發明的語法。


下面是.NET中的例子:

         (? <first> group)(?’second’group)

正如你所看到的,.NET提供兩種詞法來建立命名組:一是用尖括號“ <> ”,或者用單引號“’’”。

尖括號在字串中使用更方便,單引號在ASP程式碼中更有用,因為ASP程式碼中“ <> ”被用作HTML標籤。

要引用一個命名組,使用\k <name> 或\k’name’.當進行搜尋替換時,你可以用“${name}”來引用一個命名組。

12.正則表示式的匹配模式

本教程所討論的正則表示式引擎都支援三種匹配模式: 

            < </i> > 使正則表示式對大小寫不敏感, 

            < </s> > 開啟“單行模式”,即點號“.”匹配新行符

            < </m> > 開啟“多行模式”,即“^”和“$”匹配新行符的前面和後面的位置。


在正則表示式內部開啟或關閉模式

如果你在正則表示式內部插入修飾符(?ism),則該修飾符只對其右邊的正則表示式起作用。

(?-i)是關閉大小寫不敏感。你可以很快的進行測試。

             < <(?i)te(?-i)st> > 

應該匹配TEst,但是不能匹配teST或TEST.

13.原子組與防止回溯

在一些特殊情況下,因為回溯會使得引擎的效率極其低下。
讓我們看一個例子:要匹配這樣的字串,字串中的每個欄位間用逗號做分隔符,第12個欄位由P開頭。我們容易想到這樣的正則表示式

              < <^(.*?,){11}P> > 。

這個正則表示式在正常情況下工作的很好。但是在極端情況下,如果第12個欄位不是由P開頭,則會發生災難性的回溯。

如要搜尋的字串為

             “1,2,3,4,5,6,7,8,9,10,11,12,13”。

首先,正則表示式一直成功匹配直到第12個字元。

這時,前面的正則表示式消耗的字串為“1,2,3,4,5,6,7,8,9,10,11,”,到了下一個字元, < <P> > 並不匹配“12”。

所以引擎進行回溯,這時正則表示式消耗的字串為“1,2,3,4,5,6,7,8,9,10,11”。

繼續下一次匹配過程,下一個正則符號為點號 < <.> > ,可以匹配下一個逗號“,”。

然而 < <,> > 並不匹配字元“12”中的“1”。匹配失敗,繼續回溯。大家可以想象,這樣的回溯組合是個非常大的數量。因此可能會造成引擎崩潰。


用於阻止這樣巨大的回溯有幾種方案

    一種簡單的方案是儘可能的使匹配精確。

        用取反字符集代替點號。例如我們用如下正則表示式 < <^([^,\r\n]*,){11}P> > ,這樣可以使失敗回溯的次數下降到11次。

    另一種方案是使用原子組。

        原子組的目的是使正則引擎失敗的更快一點。因此可以有效的阻止海量回溯。

原子組的語法是 < <(?> 正則表示式)> > 。

位於(?> )之間的所有正則表示式都會被認為是一個單一的正則符號。

一旦匹配失敗,引擎將會回溯到原子組前面的正則表示式部分。前面的例子用原子組可以表達成 < <^(?> (.*?,){11})P> > 。

一旦第十二個欄位匹配失敗,引擎回溯到原子組前面的 < <^> > 。