標籤:
前面兩篇講述了Regex的基礎和一些簡單的例子,這篇將稍微深入一點探討一下Regex分組,在.NET中Regex分組是用Match類來代表的。
首先先看一段代碼:
/// <summary>/// 顯示Match內多個Group的例子/// </summary>public void ShowStructure(){ //要匹配的字串 string text = "1A 2B 3C 4D 5E 6F 7G 8H 9I 10J 11Q 12J 13K 14L 15M 16N ffee80 #800080"; //Regex string pattern = @"((/d+)([a-z]))/s+"; //使用RegexOptions.IgnoreCase枚舉值表示不區分大小寫 Regex r = new Regex(pattern, RegexOptions.IgnoreCase); //使用Regex匹配字串,僅返回一次匹配結果 Match m = r.Match(text); while (m.Success) { //顯示匹配開始處的索引值和匹配到的值 System.Console.WriteLine("Match=[" + m + "]"); CaptureCollection cc = m.Captures; foreach (Capture c in cc) { Console.WriteLine("/tCapture=[" + c + "]"); } for (int i = 0; i < m.Groups.Count; i++) { Group group = m.Groups[i]; System.Console.WriteLine("/t/tGroups[{0}]=[{1}]", i, group); for (int j = 0; j < group.Captures.Count; j++) { Capture capture = group.Captures[j]; Console.WriteLine("/t/t/tCaptures[{0}]=[{1}]", j, capture); } } //進行下一次匹配. m = m.NextMatch(); }}
這段代碼的執行效果如下:
Match=[1A ]
Capture=[1A ]
Groups[0]=[1A ]
Captures[0]=[1A ]
Groups[1]=[1A]
Captures[0]=[1A]
Groups[2]=[1]
Captures[0]=[1]
Groups[3]=[A]
Captures[0]=[A]
Match=[2B ]
Capture=[2B ]
Groups[0]=[2B ]
Captures[0]=[2B ]
Groups[1]=[2B]
Captures[0]=[2B]
Groups[2]=[2]
Captures[0]=[2]
Groups[3]=[B]
Captures[0]=[B]
..................此去省略一些結果
Match=[16N ]
Capture=[16N ]
Groups[0]=[16N ]
Captures[0]=[16N ]
Groups[1]=[16N]
Captures[0]=[16N]
Groups[2]=[16]
Captures[0]=[16]
Groups[3]=[N]
Captures[0]=[N]
通過對上面的代碼結合代碼的分析,我們得出下面的結論,在((/d+)([a-z]))/s+這個Regex裡總共包含了四個Group,即分組,按照預設的從左至右的匹配方式,其中Groups[0]代表了整個分組,其它的則是子分組,用表示如下:
在上面的代碼中是採用了Regex類的Match()方法,調用這種方法返回的是一個Match,要處理分析全部的字串,還需要在while迴圈的中通過Match類的NextMatch()方法返回下一個可能成功的匹配(可通過Match類的Success屬性來判斷是否成功匹配)。上面的代碼還可以寫成如下形式:
/// <summary>/// 使用Regex類的Matches方法所有所有的匹配/// </summary>public void Matches(){ //要匹配的字串 string text = "1A 2B 3C 4D 5E 6F 7G 8H 9I 10J 11Q 12J 13K 14L 15M 16N ffee80 #800080"; //Regex string pattern = @"((/d+)([a-z]))/s+"; //使用RegexOptions.IgnoreCase枚舉值表示不區分大小寫 Regex r = new Regex(pattern, RegexOptions.IgnoreCase); //使用Regex匹配字串,返回所有的匹配結果 MatchCollection matchCollection = r.Matches(text); foreach (Match m in matchCollection) { //顯示匹配開始處的索引值和匹配到的值 System.Console.WriteLine("Match=[" + m + "]"); CaptureCollection cc = m.Captures; foreach (Capture c in cc) { Console.WriteLine("/tCapture=[" + c + "]"); } for (int i = 0; i < m.Groups.Count; i++) { Group group = m.Groups[i]; System.Console.WriteLine("/t/tGroups[{0}]=[{1}]", i, group); for (int j = 0; j < group.Captures.Count; j++) { Capture capture = group.Captures[j]; Console.WriteLine("/t/t/tCaptures[{0}]=[{1}]", j, capture); } } }}
上面的這段代碼和採用While迴圈遍曆所有匹配的結果是一樣的,在實際情況中有可能出現不需要全部匹配而是從某一個位置開始匹配的情況,比如從第32個字元處開始匹配,這種要求可以通過Match()或者Matches()方法的重載方法來實現,僅需要將剛才的執行個體代碼中的MatchCollection matchCollection = r.Matches(text);改為MatchCollection matchCollection = r.Matches(text,48);就可以了。
輸出結果如下:
Match=[5M ]
Capture=[5M ]
Groups[0]=[5M ]
Captures[0]=[5M ]
Groups[1]=[5M]
Captures[0]=[5M]
Groups[2]=[5]
Captures[0]=[5]
Groups[3]=[M]
Captures[0]=[M]
Match=[16N ]
Capture=[16N ]
Groups[0]=[16N ]
Captures[0]=[16N ]
Groups[1]=[16N]
Captures[0]=[16N]
Groups[2]=[16]
Captures[0]=[16]
Groups[3]=[N]
Captures[0]=[N]
注意上面的MatchCollection matchCollection = r.Matches(text,48)表示從text字串的位置48處開始匹配,要注意位置0位於整個字串的之前,位置1位於字串中第一個字元之後第二個字元之前,如下(注意是字串“1A”與“2B”之間有空格):
在text的位置48處正好是15M中的5處,因此返回的第一個Match是5M而不是15M。這裡還繼續拿出第一篇中的圖來,如下:
從可以看出Capture、Group及Match類之間存在繼承關係,處在繼承關係頂端的Capture類中就定義了Index、Length和Value屬性,其中Index表示原始字串中發現捕獲子字串的第一個字元的出現位置,Length屬性工作表示子字串的長度,而Value屬性工作表示從原始字串中捕獲的子字串,利用這些屬性可以實現一些比較複雜的應用。例如在現在還有很多論壇仍沒有使用所見即所得 (WYSIWYG)的線上編輯器,而是使用了一種UBB編碼的編輯器,使用所見即所得 (WYSIWYG)的編輯器存在著一定的安全風險,比如可以在原始碼中嵌入js代碼或者其它惡意代碼,這樣瀏覽者訪問時就會帶來安全問題,而使用UBB代碼就不會代碼這個問題,因為UBB程式碼封裝含了有限的、但不影響常規使用的標記並且支援UBB代碼的編輯器不允許直接在字串中出現HTML代碼,也而就避免惡意指令碼攻擊的問題。在支援UBB代碼的編輯器中輸入的文本在存入資料庫中儲存的形式是UBB編碼,顯示的時候需要將UBB編碼轉換成HTML代碼,例如下面的一段代碼就是UBB編碼:
[url]http://zhoufoxcn.blog.51cto.com[/url][url=http://blog.csdn.net/zhoufoxcn]周公的專欄[/url]
下面通過例子示範如何將上面的UBB編碼轉換成HTML代碼:
下面通過例子示範如何將上面的UBB編碼轉換成HTML代碼:/// <summary>/// 下面的代碼實現將文本中的UBB超級連結代碼替換為HTML超級連結代碼/// </summary>public void UBBDemo(){ string text = "[url=http://zhoufoxcn.blog.51cto.com][/url][url=http://blog.csdn.net/zhoufoxcn]周公的專欄[/url]"; Console.WriteLine("原始UBB代碼:" + text); Regex regex = new Regex(@"(/[url=([ /S/t]*?)/])([^[]*)(/[//url/])", RegexOptions.IgnoreCase); MatchCollection matchCollection = regex.Matches(text); foreach (Match match in matchCollection) { string linkText = string.Empty; //如果包含了連結文字,如第二個UBB代碼中存在連結名稱,則直接使用連結名稱 if (!string.IsNullOrEmpty(match.Groups[3].Value)) { linkText = match.Groups[3].Value; } else//否則使用連結作為連結名稱 { linkText = match.Groups[2].Value; } text = text.Replace(match.Groups[0].Value, "<a href="/" mce_href="/""" + match.Groups[2].Value + "/" target=/"_blank/">" + linkText + "</a>"); } Console.WriteLine("替換後的代碼:"+text);}
程式執行結果如下:
原始UBB代碼:[url=http://zhoufoxcn.blog.51cto.com][/url][url=http://blog.csdn.net/zhoufoxcn]周公的專欄[/url]
替換後的代碼:<a href="http://zhoufoxcn.blog.51cto.com" target="_blank">http://zhoufoxcn.blog.51cto.com</a><a href="http://blog.csdn.net/zhoufoxcn"target="_blank">周公的專欄</a>
上面的這個例子就稍微複雜點,對於初學Regex的朋友來說,可能有點難於理解,不過沒有關係,後面我會講講Regex。在實際情況下,可能通過match.Groups[0].Value這種方式不太方便,就想在訪問DataTable時寫string name=dataTable.Rows[i][j]這種方式一樣,一旦再次調整,這種通過索引的方式極容易出錯,實際上我們也可以採用名稱而不是索引的放來來訪問Group分組,這個也會在以後的篇幅中去講。
C#Regex編程(三):Match類和Group類用法