1. 程式人生 > >C# 正則表示式及常用正則表示式

C# 正則表示式及常用正則表示式

匹配模式+環視(順序環視、逆序環視)+貪婪與非貪婪
 
RegexOptions.Multiline
“^”匹配結果分析
在不開啟多行模式時,“^”只匹配字串的開始位置,也就是位置0。
在開啟了多行模式後,“^”匹配字串開始位置和每個“\n”之後的行起始位置。
 
“$”匹配結果分析
在不開啟多行模式時,如果字元結尾是“\n”,那麼“$”會匹配結尾“\n”之前和結束兩個位置。
在開啟多行模式後,“$”匹配每行“\n”之前的位置和字串結束位置。
 
需要注意的是,在.NET中,無論是否開啟多行模式,“^”和“$”匹配的都只是一個位置,是零寬度的。其它語言中“^”和“$”的意義可能會有所不同。
只有在正則表示式中涉及到多行的“
^”和“$”的匹配時,才使用Multiline模式。 RegexOptions.Compiled Compiled改變的是.NET中正則表示式的編譯方式。啟用了Compiled模式,會延長啟動時間,佔用更多的記憶體,會提高匹配速度。當然,對最終效能的影響,需要根據具體問題綜合考慮的。這一模式也是被“濫”用最多的模式之一。 程式執行過程中,第一次遇到正則表示式,需要載入正則引擎,對正則表示式進行必要的語法檢查,並做適當的優化,最後把它轉換為適合正則引擎應用的形式。這種“解析”過程,對於複雜的正則表示式,頻繁呼叫或是匹配較大的資料來源時,對效率的影響較大。 這時可以在構建正則表示式時開啟Compiled模式。這樣做會將正則表示式直接編譯為MSIL程式碼,在正則匹配過程中,可以由JIT優化為更快的本地機器程式碼,獲得更高的匹配速度。但這種方式會降低正則的解析速度,佔用更多的記憶體,而且它佔用的記憶體在程式執行過程中會一直佔用,無法釋放。 什麼場景下使用Compiled模式,需要根據實際情況具體問題具體分析,一般來說,以下場景不適合使用Compiled模式:
1.對匹配效率沒有要求的場景; 2.非常簡單的正則表示式; 3.極少呼叫的方法中宣告的正則表示式; 4.迴圈體中宣告的正則表示式(除了動態生成的正則表示式,否則不要在迴圈體內宣告正則表示式); 5.靜態方法中宣告的正則表示式(靜態方法每次呼叫都需要重新編輯正則表示式,使用Compiled模式只會降低效率)。 RegexOptions.RightToLeft RightToLeft改變的是正則表示式匹配的順序,從右到左進行匹配 一個由字母組成的字串,最長14位,要求每隔2位加一個逗號,最左邊不加,求一個好的演算法 例:“abcdefg” 返回“a,bc,de,fg” 程式碼實現: string
test = "abcdefg"; string result = Regex.Replace(test, @"(?<!^)[a-zA-Z]{2}", ",$0", RegexOptions.RightToLeft); RegexOptions.ExplicitCapture 這一模式改變的是普通捕獲組的匹配行為。將普通捕獲組解釋為非捕獲組,只有顯式命名的命名捕獲組才當作捕獲組使用。 捕獲組的作用是將括號()內子表示式匹配到的內容儲存到記憶體中一個組裡,供以後引用,在.NET中捕獲組有兩種形式 (Expression) 普通捕獲組 (?<name>Expression) 命名捕獲組 其它形式的(?...)都不是捕獲組。 但是(Expression)這種捕獲組語法規則也帶來一個副作用,在一些不得不使用()的場合,會預設為使用了捕獲組,將匹配到的內容儲存到記憶體中,而有些情況下這些內容並不需要關心的,浪費了系統資源,降低了匹配效率,所以才有了非捕獲組(?:Expression)的出現,來抵消這一副作用。而非捕獲組帶來的另一個副作用的就是可讀性的降低。 string test = "<li title=\"截至2009-07-28 20:45:49,使用者的總技術分為:5988;截至2009-07-26日,使用者的總技術分排名為:4133\">(...)</li>"; Regex reg = new Regex(@"([01][0-9]|2[0-3])(:[0-5][0-9]){2}", RegexOptions.ExplicitCapture); MatchCollection mc = reg.Matches(test); string s = ""; foreach (Match m in mc) { s += m.Value + "\n"; s += m.Groups[1].Value + "\n"; s += m.Groups[2].Value + "\n"; } Console.Write(s); 未開啟RegexOptions.ExplicitCapture /* 20:45:49 20 :49 */ 開啟的結果是 /* 20:45:49 */ (?imnsx-imnsx:)形式 string[] test = new string[] { "Abc", "AbcdefGHIjklmn", "abcdefghijklmn" }; Regex reg = new Regex(@"^[A-Z](?i:[A-Z]{9,19})$"); string str = ""; foreach (string s in test) { str += "源字串:" + s.PadRight(15, ' ') + " 匹配結果: " + reg.IsMatch(s) + "\n"; } Console.WriteLine(str); /*--------輸出-------- 源字串: Abc 匹配結果: False 源字串: AbcdefGHIjklmn 匹配結果: True 源字串: abcdefghijklmn 匹配結果: False */ 語法:(?-i:Expression) 這種語法規則表達為括號內的子表示式關閉忽略大小寫模式。通常與全域性匹配模式配合使用,表示全域性為忽略大小寫的,區域性為嚴格區分大小寫。 string test = "<DIV id=\"Test\" class=\"create\">first</div> and <DIV id=\"TEST\" class=\"delete\">second</div>"; Regex reg = new Regex(@"<div id=""(?-i:TEST)""[^>]*>[\w\s]+</div>", RegexOptions.IgnoreCase); string str = ""; MatchCollection mc = reg.Matches(test); foreach (Match m in mc) { str += m.Value + "\n"; } Console.WriteLine(str); /*--------輸出-------- <DIV id="TEST" class="delete">second</div> */ 環視基礎 表示式 說明 (?<=Expression) 逆序肯定環視,表示所在位置左側能夠匹配Expression (?<!Expression) 逆序否定環視,表示所在位置左側不能匹配Expression (?=Expression) 順序肯定環視,表示所在位置右側能夠匹配Expression (?!Expression) 順序否定環視,表示所在位置右側不能匹配Expression string str = "aa<p>one</p>bb<div>two</div>cc"; foreach (Match item in Regex.Matches(str, @"<(?!/?p\b)[^>]+>")) { Console.WriteLine(item.Value); } /* <div> </div> */ string str = "<div>a test</div><div>1</div>"; foreach (Match item in Regex.Matches(str, @"(?<=<div>)[^<]+(?=</div>)")) { Console.WriteLine(item.Value); } /* a test 1 */ double[] data = new double[] { 0, 12, 123, 1234, 12345, 123456, 1234567, 123456789, 1234567890, 12.345, 123.456, 1234.56, 12345.6789, 123456.789, 1234567.89, 12345678.9 }; string s = ""; foreach (double d in data) { s += "源字串:" + d.ToString().PadRight(15) + "格式化:" + Regex.Replace(d.ToString(), @"(?<=\d)(?<!\.\d*)(?=(?:\d{3})+(?:\.\d+|$))", ",") + "\n"; } Console.Write(s); 結果: 源字串:0 格式化:0 源字串:12 格式化:12 源字串:123 格式化:123 源字串:1234 格式化:1,234 源字串:12345 格式化:12,345 源字串:123456 格式化:123,456 源字串:1234567 格式化:1,234,567 源字串:123456789 格式化:123,456,789 源字串:1234567890 格式化:1,234,567,890 源字串:12.345 格式化:12.345 源字串:123.456 格式化:123.456 源字串:1234.56 格式化:1,234.56 源字串:12345.6789 格式化:12,345.6789 源字串:123456.789 格式化:123,456.789 源字串:1234567.89 格式化:1,234,567.89 源字串:12345678.9 格式化:12,345,678.9 實現分析: 首先根據需求可以確定是把一些特定的位置替換為“,”,接下來就是分析並找到這些位置的規律,並抽象出來以正則表示式來表示。 1、這個位置的左側必須為數字 2、這個位置右側到出現“.”或結尾為止,必須是數字,且數字的個數必須為3的倍數 3、這個位置左側相隔任意個數字不能出現“.” 由以上三條,就可以完全確定這些位置,只要實現以上三條,組合一下正則表示式就可以了。 根據分析,最終匹配的結果是一個位置,所以所有子表示式都要求是零寬度。 1、是對當前所在位置左側附加的條件,所以要用到逆序環視,因為要求必須出現,所以是肯定的,符合這一條件的子表示式即為“(?<=\d)” 2、是對當前所在位置右側附加的條件,所以要用到順序環視,也是要求出現,所以是肯定的,是數字,且個數為3的倍數,即“(?=(?:\d{3})*)”,到出現“.”或結尾為止,即“(?=(?:\d{3})*(?:\.|$))” 3、是對當前所在位置左側附加的條件,所以要用到逆序環視,因為要求不能出現,所以是否定的,即“(?<!\.\d*)” 因為零寬度的子表示式是非互斥的,最後匹配的都是同一個位置,所以先後順序是不影響最後的匹配結果的,可以任意組合,只是習慣上把逆序環視寫在左側,順序環視寫在右側。 貪婪與非貪婪模式匹配 string str = "aa<div>test1</div>bb<div>test2</div>cc"; foreach (Match item in Regex.Matches(str, @"<div>.*</div>")) { Console.WriteLine(item.Value); } /*貪婪模式,很好理解,儘量多匹配 <div>test1</div>bb<div>test2</div> */ 如果正則表示式是<div>.*?</div> /*非貪婪模式,也叫懶惰模式,就是懶的意思,匹配到了就不會再向後匹配 <div>test1</div> <div>test2</div> */ 貪婪非貪婪的比較以及優化 string str =@"The phrase ""regular expression"" is called ""Regex"" for short."; foreach (Match item in Regex.Matches(str, @""".*""")) { Console.WriteLine(item.Value); } 一、使用”.*”這種匹配的結果不符合:"regular expression" is called "Regex" 二、使用”.*?”的結果: "regular expression" "Regex" OK,不過進行了四次回溯 三、使用這種[^"]*匹配OK,沒有回溯 四、對三的改進,固化分組 (?>[^"]*),匹配效率最好 再看一例,獲取img標籤的src內容: string str =@"<img class=""test"" src=""/img/logo.gif"" title=""測試"" />"; foreach (Match item in Regex.Matches(str, @"<img\b.*?src=""(.*?)"".*?")) { Console.WriteLine(item.Groups[1].Value); } 使用的是非貪婪模式,我們通過排除型字元組轉換為貪婪模式,提高匹配效率 @"<img\b.*?src=""([^""]*)""[^>]*" img與src之間的非貪婪通過順序環視來轉化 @"<img\b(?:(?!src=).)*src=""([^""]*)""[^>]*" “(?!src=).”表示這樣一個字元,從它開始,右側不能是字元序列“src=”,而“(?:(?!src=).)*”就表示符合上面規則的字元,有0個或無限多個。這樣就達到排除字元序列的目的,實現的效果同排除型字元組一樣,只不過排除型字元組排除的是一個或多個字元,而這種環視結構排除的是一個或多個有序的字元序列。 但是以順序環視的方式排除字元序列,由於在匹配每一個字元時,都要進行較多的判斷,所以相對於非貪婪模式,是提升效率還是降低效率,要根據實際情況進行分析。對於簡單的正則表示式,或是簡單的源字串,一般來說是非貪婪模式效率高些,而對於數量較大源字串,或是複雜的正則表示式,一般來說是貪婪模式效率高些。 PS:一般都是貪婪模式+固化分組,當然也需要看具體情況。 一些正則的實踐篇 清除掉iframe+javascript+css的惡意指令碼 http://rczjp.cn/HTML/101210/20105010115029.html 正則擷取URL網址 http://rczjp.cn/HTML/101218/20102718032702.html 常見的正則 http://rczjp.cn/HTML/081119/20082119022155.html 此文章來自原作者:http://blog.csdn.net/lxcnn/ 具體詳情去過客的blog參看。 下面是作者寫的NFA引擎匹配原理,講解的很詳細,值得參看: http://blog.csdn.net/lxcnn/archive/2009/06/28/4304651.aspx