C#正则表达式编程(三):Match类和Group类用法

简介:
前面两篇讲述了正则表达式的基础和一些简单的例子,这篇将稍微深入一点探讨一下正则表达式分组,在.NET中正则表达式分组是用Math类来代表的。
首先先看一段代码:
/// <summary>
/// 显示Match内多个Group的例子
/// </summary>
InBlock.gif public  void ShowStructure()
InBlock.gif{
InBlock.gif         //要匹配的字符串
InBlock.gif         string text =  "1A 2B 3C 4D 5E 6F 7G 8H 9I 10J 11Q 12J 13K 14L 15M 16N ffee80 #800080";
InBlock.gif         //正则表达式
InBlock.gif         string pattern =  @"((\d+)([a-z]))\s+";
InBlock.gif         //使用RegexOptions.IgnoreCase枚举值表示不区分大小写
InBlock.gif        Regex r =  new Regex(pattern, RegexOptions.IgnoreCase);
InBlock.gif         //使用正则表达式匹配字符串,仅返回一次匹配结果
InBlock.gif        Match m = r.Match(text);
InBlock.gif         while (m.Success)
InBlock.gif        {
InBlock.gif
                 //显示匹配开始处的索引值和匹配到的值
InBlock.gif                System.Console.WriteLine( "Match=[" + m +  "]");
InBlock.gif                CaptureCollection cc = m.Captures;
InBlock.gif                 foreach (Capture c  in cc)
InBlock.gif                {
InBlock.gif                        Console.WriteLine( "\tCapture=[" + c +  "]");
InBlock.gif                }
InBlock.gif                 for ( int i = 0; i < m.Groups.Count; i++)
InBlock.gif                {
InBlock.gif                        Group group = m.Groups[i];
InBlock.gif                        System.Console.WriteLine( "\t\tGroups[{0}]=[{1}]", i, group);
InBlock.gif                         for ( int j = 0; j < group.Captures.Count; j++)
InBlock.gif                        {
InBlock.gif                                Capture capture = group.Captures[j];
InBlock.gif                                Console.WriteLine( "\t\t\tCaptures[{0}]=[{1}]", j, capture);
InBlock.gif                        }
InBlock.gif                }
InBlock.gif                 //进行下一次匹配.
InBlock.gif                m = m.NextMatch();
InBlock.gif        }
InBlock.gif}

这段代码的执行效果如下:
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属性来判断是否成功匹配)。上面的代码还可以写成如下形式:
/// <summary>
/// 使用Regex类的Matches方法所有所有的匹配
/// </summary>
InBlock.gif public  void Matches()
InBlock.gif{
InBlock.gif         //要匹配的字符串
InBlock.gif         string text =  "1A 2B 3C 4D 5E 6F 7G 8H 9I 10J 11Q 12J 13K 14L 15M 16N ffee80 #800080";
InBlock.gif         //正则表达式
InBlock.gif         string pattern =  @"((\d+)([a-z]))\s+";
InBlock.gif         //使用RegexOptions.IgnoreCase枚举值表示不区分大小写
InBlock.gif        Regex r =  new Regex(pattern, RegexOptions.IgnoreCase);
InBlock.gif         //使用正则表达式匹配字符串,返回所有的匹配结果
InBlock.gif        MatchCollection matchCollection = r.Matches(text);
InBlock.gif         foreach (Match m  in matchCollection)
InBlock.gif        {
InBlock.gif                 //显示匹配开始处的索引值和匹配到的值
InBlock.gif                System.Console.WriteLine( "Match=[" + m +  "]");
InBlock.gif                CaptureCollection cc = m.Captures;
InBlock.gif                 foreach (Capture c  in cc)
InBlock.gif                {
InBlock.gif                        Console.WriteLine( "\tCapture=[" + c +  "]");
InBlock.gif                }
InBlock.gif                 for ( int i = 0; i < m.Groups.Count; i++)
InBlock.gif                {
InBlock.gif                        Group group = m.Groups[i];
InBlock.gif                        System.Console.WriteLine( "\t\tGroups[{0}]=[{1}]", i, group);
InBlock.gif                         for ( int j = 0; j < group.Captures.Count; j++)
InBlock.gif                        {
InBlock.gif                                Capture capture = group.Captures[j];
InBlock.gif                                Console.WriteLine( "\t\t\tCaptures[{0}]=[{1}]", j, capture);
InBlock.gif                        }
InBlock.gif                }
InBlock.gif        }
InBlock.gif}

上面的这段代码和采用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属性表示从原始字符串中捕获的子字符串,利用这些属性可以实现一些比较复杂的应用。例如在现在还有很多论坛仍没有使用所见即所得的在线编辑器,而是使用了一种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代码:
/// <summary>
/// 下面的代码实现将文本中的UBB超级链接代码替换为HTML超级链接代码
/// </summary>
InBlock.gif public  void UBBDemo()
InBlock.gif{
InBlock.gif         string text =  "[url=http://zhoufoxcn.blog.51cto.com][/url][url=http://blog.csdn.net/zhoufoxcn]周公的专栏[/url]";
InBlock.gif        Console.WriteLine("原始UBB代码:" + text);
InBlock.gif        Regex regex = new Regex(@"(\[url=([ \S\t]*?)\])([^[]*)(\[\/url\])", RegexOptions.IgnoreCase);
InBlock.gif        MatchCollection matchCollection = regex.Matches(text);
InBlock.gif        foreach (Match match in matchCollection)
InBlock.gif        {
InBlock.gif                string linkText = string.Empty;
InBlock.gif                //如果包含了链接文字,如第二个UBB代码中存在链接名称,则直接使用链接名称
InBlock.gif                if (!string.IsNullOrEmpty(match.Groups[3].Value))
InBlock.gif                {
InBlock.gif                        linkText = match.Groups[3].Value;
InBlock.gif                }
InBlock.gif                else//否则使用链接作为链接名称
InBlock.gif                {
InBlock.gif                        linkText = match.Groups[2].Value;
InBlock.gif                }
InBlock.gif                text = text.Replace(match.Groups[0].Value, "<a href=\"" + match.Groups[2].Value + "\" target=\"_blank\">"+ linkText + "</a>");
InBlock.gif        }
InBlock.gif        Console.WriteLine("替换后的代码:"+text);
InBlock.gif
}

程序执行结果如下:

原始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,如需转载请自行联系原作者


相关文章
|
7天前
|
存储 C# 索引
C# 一分钟浅谈:数组与集合类的基本操作
【9月更文挑战第1天】本文详细介绍了C#中数组和集合类的基本操作,包括创建、访问、遍历及常见问题的解决方法。数组适用于固定长度的数据存储,而集合类如`List<T>`则提供了动态扩展的能力。文章通过示例代码展示了如何处理索引越界、数组长度不可变及集合容量不足等问题,并提供了解决方案。掌握这些基础知识可使程序更加高效和清晰。
30 2
|
6天前
|
C# 数据安全/隐私保护
C# 一分钟浅谈:类与对象的概念理解
【9月更文挑战第2天】本文从零开始详细介绍了C#中的类与对象概念。类作为一种自定义数据类型,定义了对象的属性和方法;对象则是类的实例,拥有独立的状态。通过具体代码示例,如定义 `Person` 类及其实例化过程,帮助读者更好地理解和应用这两个核心概念。此外,还总结了常见的问题及解决方法,为编写高质量的面向对象程序奠定基础。
11 2
|
16天前
|
Python
告别死记硬背:掌握Python正则表达式re模块的高效应用&[面向百度编程]
Python中正则表达式的高效应用,通过内置的`re`模块,讲解了如何匹配、提取和替换字符串,并提供了相关示例代码,同时提倡通过实践来掌握正则表达式的使用,而不是仅仅依赖网络搜索。
29 1
|
7天前
|
图形学 C# 开发者
全面掌握Unity游戏开发核心技术:C#脚本编程从入门到精通——详解生命周期方法、事件处理与面向对象设计,助你打造高效稳定的互动娱乐体验
【8月更文挑战第31天】Unity 是一款强大的游戏开发平台,支持多种编程语言,其中 C# 最为常用。本文介绍 C# 在 Unity 中的应用,涵盖脚本生命周期、常用函数、事件处理及面向对象编程等核心概念。通过具体示例,展示如何编写有效的 C# 脚本,包括 Start、Update 和 LateUpdate 等生命周期方法,以及碰撞检测和类继承等高级技巧,帮助开发者掌握 Unity 脚本编程基础,提升游戏开发效率。
20 0
|
1月前
|
C#
C#中的类和继承
C#中的类和继承
30 6
|
1月前
|
存储 C#
揭秘C#.Net编程秘宝:结构体类型Struct,让你的数据结构秒变高效战斗机,编程界的新星就是你!
【8月更文挑战第4天】在C#编程中,结构体(`struct`)是一种整合多种数据类型的复合数据类型。与类不同,结构体是值类型,意味着数据被直接复制而非引用。这使其适合表示小型、固定的数据结构如点坐标。结构体默认私有成员且不可变,除非明确指定。通过`struct`关键字定义,可以包含字段、构造函数及方法。例如,定义一个表示二维点的结构体,并实现计算距离原点的方法。使用时如同普通类型,可通过实例化并调用其成员。设计时推荐保持结构体不可变以避免副作用,并注意装箱拆箱可能导致的性能影响。掌握结构体有助于构建高效的应用程序。
47 7
|
19天前
|
Java C# 索引
C# 面向对象编程(一)——类
C# 面向对象编程(一)——类
25 0
|
20天前
|
安全 C# 开发者
【C# 多线程编程陷阱揭秘】:小心!那些让你的程序瞬间崩溃的多线程数据同步异常问题,看完这篇你就能轻松应对!
【8月更文挑战第18天】多线程编程对现代软件开发至关重要,特别是在追求高性能和响应性方面。然而,它也带来了数据同步异常等挑战。本文通过一个简单的计数器示例展示了当多个线程无序地访问共享资源时可能出现的问题,并介绍了如何使用 `lock` 语句来确保线程安全。此外,还提到了其他同步工具如 `Monitor` 和 `Semaphore`,帮助开发者实现更高效的数据同步策略,以达到既保证数据一致性又维持良好性能的目标。
24 0
|
23天前
|
开发框架 .NET 编译器
C# 中的记录(record)类型和类(class)类型对比总结
C# 中的记录(record)类型和类(class)类型对比总结