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.原子組與防止回溯 < <^(.*?,){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> > 。 一旦第十二個欄位匹配失敗,引擎回溯到原子組前面的 < <^> > 。 |