天天看點

C#正規表達式程式設計(三):Match類和Group類用法

前面兩篇講述了正規表達式的基礎和一些簡單的例子,這篇将稍微深入一點探讨一下正規表達式分組,在.NET中正規表達式分組是用Math類來代表的。

首先先看一段代碼:

/// <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";

        //正規表達式

        string pattern = @"((\d+)([a-z]))\s+";

        //使用RegexOptions.IgnoreCase枚舉值表示不區分大小寫

        Regex r = new Regex(pattern, RegexOptions.IgnoreCase);

        //使用正規表達式比對字元串,僅傳回一次比對結果

        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+這個正規表達式裡總共包含了四個Group,即分組,按照預設的從左到右的比對方式,其中Groups[0]代表了整個分組,其它的則是子分組,用示意圖表示如下:

在上面的代碼中是采用了Regex類的Match()方法,調用這種方法傳回的是一個Match,要處理分析全部的字元串,還需要在while循環的中通過Match類的NextMatch()方法傳回下一個可能成功的比對(可通過Match類的Success屬性來判斷是否成功比對)。上面的代碼還可以寫成如下形式:

/// 使用Regex類的Matches方法所有所有的比對

public void Matches()

        //使用正規表達式比對字元串,傳回所有的比對結果

        MatchCollection matchCollection = r.Matches(text);

        foreach (Match m in matchCollection)

上面的這段代碼和采用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]

        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屬性表示從原始字元串中捕獲的子字元串,利用這些屬性可以實作一些比較複雜的應用。例如在現在還有很多論壇仍沒有使用所見即所得的線上編輯器,而是使用了一種UBB編碼的編輯器,使用所見即所得的編輯器存在着一定的安全風險,比如可以在源代碼中嵌入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超級連結代碼

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=\"" + 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>

上面的這個例子就稍微複雜點,對于初學正規表達式的朋友來說,可能有點難于了解,不過沒有關系,後面我會講講正規表達式。在實際情況下,可能通過match.Groups[0].Value這種方式不太友善,就想在通路DataTable時寫string name=dataTable.Rows[i][j]這種方式一樣,一旦再次調整,這種通過索引的方式極容易出錯,實際上我們也可以采用名稱而不是索引的放來來通路Group分組,這個也會在以後的篇幅中去講。

本文轉自周金橋51CTO部落格,原文連結: http://blog.51cto.com/zhoufoxcn/281956,如需轉載請自行聯系原作者