括号
正则表达式中的括号能将多个字符或者表达式当做一组,即将他们看成一个整体。这样量词就可以修饰这一组表达式。阅读本章前,建议先下载我于CSDN上传的示例代码,下载无需分数,下载链接。
1.分组
假设我们要匹配偶数个数字,如何匹配?尝试"^\d{2}+$"来匹配。"\d{2}"匹配2个数字,"+"匹配至少一次,两者组合起来应该是偶数个数字。但是由下例可看出这个正则表达式并不合法。只能尝试修改这个正则表达式为"^(\d{2})+$"。将"\d{2}"看做一个分组,再来使用量词。
Regex.IsMatch("12",@"^\d{2}+$"); //抛出异常:System.ArgumentException //异常信息:正在分析“^\d{2}+$”- 嵌套限定符 +。 Regex.IsMatch("12",@"^(\d{2})+$");//true
2.捕获分组
捕获分组十分有用,他可以捕获匹配正则表达式的子字符串。分组其实是带有捕获功能的,例如分组"(\d)"匹配呢数字"5",则可以通过匹配对象获得"5"这个值。现在有几个匹配日期格式的正则表达式"(\d{4}-\d{2}-\d{2})","(\d{4}-\d{2})-\d{2}","(\d{4})-(\d{2})-(\d{2})"和一个十分没有规律的输入字符串"2015-06-15-20-30-2014-02-28-01-015-2015-08-09-00"。我们要从中提取出一些日期,不同的正则表达式会让程序输出不同的结果,但是他们均会匹配出他们想要的三个日期。"2015-06-15","2014-02-28","2015-08-09"。
1 //一个用于输出匹配子字符串的辅助方法 2 public static void ShowAllMatch(Match match, bool isCapture) 3 { 4 int MatchCount = 0;//匹配次数 5 while (true) 6 { 7 if (match.Success) 8 { 9 MatchCount++; 10 Console.WriteLine("第{0}个匹配的字符串为:{1}", MatchCount.ToString(), match.Value); 11 int GroupIndex = 0; 12 /* 13 * 对于任意一个Match对象,其Gruops集合中至少有一个元素,下标为0。 14 * 若Match对象能成功匹配子字符串,则下标0的位置默认存储整个匹配的字符串。若不成功则为空。 15 * 若通过下标寻找不存在的项,则只返回空字符串,不会报错。 16 * 其余下标对应正则表达式中括号出现的位置。如第一个Gruops[1],对应匹配第一个括号的分组。 17 */ 18 foreach (Group group in match.Groups) 19 { 20 Console.WriteLine("分组下标:{0},分组的值为:{1}", GroupIndex.ToString(), group.Value); 21 /* 22 * 当在括号分组后加上量词时(如'(\d){m,n}'这种形式),可通过Capture类获得每一次捕获的字符串。 23 * 如,若匹配了字符串"2015",则Capture会分别捕获,"2","0","1","5",group.Value的值为"5" 24 * 此时,group.Value仅获取最后捕获的子字符串。 25 */ 26 if (isCapture) 27 { 28 int CaptureIndex = 0; 29 foreach (Capture capture in group.Captures) 30 { 31 Console.WriteLine("分组下标:{0},第{1}个捕获的子字符串为:{2}", GroupIndex.ToString(), CaptureIndex.ToString(), capture.Value); 32 CaptureIndex++; 33 } 34 } 35 GroupIndex++; 36 } 37 match = match.NextMatch();//获取从该匹配位置结束之后的下一个匹配对象。 38 Console.WriteLine(); 39 } 40 else 41 { 42 if (MatchCount == 0) 43 Console.WriteLine("没有匹配项!"); 44 else 45 Console.WriteLine("匹配结束!"); 46 break; 47 } 48 } 49 } 50 51 /*Main方法中的调用*/ 52 53 Console.WriteLine("---捕获分组示例1---"); 54 string InputB = "2015-06-15-20-30-2014-02-28-01-015-2015-08-09-00"; 55 string RegexStrB = @"(\d{4})-(\d{2})-\d{2}";//匹配一个日期,格式为YYYY-MM-DD。再通过分组获取匹配字符串中的年份和月份。 56 Regex RegexB = new Regex(RegexStrB); 57 Match MatchB = RegexB.Match(InputB); 58 ShowAllMatch(MatchB, false); 59 Console.WriteLine(); 60 61 Console.WriteLine("---捕获分组示例2---"); 62 string InputC = "2015-06-15-20-30-2014-02-28-01-015-2015-08-09-00"; 63 string RegexStrC = @"(\d){4}-(\d){2}-\d{2}";//匹配一个日期,格式为YYYY-MM-DD。再通过分组获取匹配字符串中的年份和月份。 64 Regex RegexC = new Regex(RegexStrC); 65 Match MatchC = RegexC.Match(InputC); 66 ShowAllMatch(MatchC, true); 67 Console.WriteLine(); 68 69 Console.WriteLine("---捕获分组示例3---"); 70 string InputD = "2015-06-15-20-30-2014-02-28-01-015-2015-08-09-00"; 71 /* 72 * 匹配一个日期,格式为YYYY-MM-DD。再通过分组获取匹配字符串中的年份和月份。 73 * 若括号之间有嵌套,则Group的下标先算外层括号,再算内层。 74 */ 75 string RegexStrD = @"((\d){4}-(\d){2})-(\d){2}"; 76 Regex RegexD = new Regex(RegexStrD); 77 Match MatchD = RegexD.Match(InputD); 78 ShowAllMatch(MatchD, true); 79 Console.WriteLine();
以上代码由各位复制到自己的机器查看运行结果。以下代码给出直观的捕获匹配值的例子。
Regex regex = new Regex(@"(\d{4}-\d{2}-\d{2})"); Match match = regex.Match("2015-06-15-20-30-2014-02-28-01-015-2015-08-09-00"); while(true) { if (match.Success) { Console.WriteLine(match.Value); match = match.NextMatch();//匹配下一个符合的子字符串 } else break; } //一共输出3个结果。 //2015-06-15 //2014-02-28 //2015-08-09
Match类是匹配结果的对象。可通过Match.Success判断匹配是否成功,通过Match.Value来获得符合匹配的子字符串。
3.反向引用
如果我们要判断一个英文单词是否有相邻两个字母重复。例如"book","sleep"等。我们可以使用反向引用这个特性。其正则表达式语法"(regStr)\num",其中regStr为子正则表达式,num为需要引用的括号的位置。
Regex regex = new Regex(@"([a-z])\1"); regex.IsMatch("book");//true regex.IsMatch("sleep");//true regex.IsMatch("where");//false
由于\num容易引起不确定,因为有可能转换为对应的ascii码。所以.NET还可以以"(regStr)\k<num>"这样的语法确保反向引用。
Regex regex = new Regex(@"([a-z])\k<1>"); regex.IsMatch("book");//true regex.IsMatch("sleep");//true regex.IsMatch("where");//false
4.命名分组
当分组太多的时候,容易出现计算分组下标错误的情况。因此可以给分组命名,直接通过名字来获取分组的值。正则表达式语法"(?<name>regStr)",name的值由你自己决定。
Regex regex = new Regex(@"(?<Year>\d{4})-(?<Month>\d{2})-(?<Day>\d{2})"); Match match = regex.Match("2015-06-15"); Console.WriteLine(match.Groups["Day"].Value);//"15" Console.WriteLine(match.Groups["Year"].Value);//"2015" Console.WriteLine(match.Groups["Month"].Value);//"06" //也可以使用下标查找,并不影响。 Console.WriteLine(match.Groups[0].Value);//"2015-06-15" Console.WriteLine(match.Groups[1].Value);//"2015" Console.WriteLine(match.Groups[2].Value);//"06" Console.WriteLine(match.Groups[3].Value);//"15"
5.非捕获分组
由于捕获会造成性能的损耗,当我们仅想将多个正则表达式组成一组而不想捕获他的值时,可使用非捕获分组。正则表达式语法"(?:regStr)"。
Regex regex = new Regex(@"(?:\d{4})-(?:\d{2})-(?:\d{2})"); Match match = regex.Match("2015-06-15"); Console.WriteLine(match.Groups[0].Value);//"2015-06-15" Console.WriteLine(match.Groups[1].Value);// "" 没有值,但是不会报错 Console.WriteLine(match.Groups[2].Value);// "" 没有值,但是不会报错 Console.WriteLine(match.Groups[3].Value);// "" 没有值,但是不会报错
6.贪婪匹配与懒惰匹配
对于一个输入字符串"123456789",如果用"(\d*)"来匹配,则整个字符串都将被捕获了。我们可以看出这个正则表达式十分贪婪,如果输入一个一百位的数字他也是会捕获回来的。如果我们要尽可能少的捕获子字符串呢?那么可以在量词后添加"?",懒惰匹配正则表达式语法"regStr{n,m}?"。
Regex regex = new Regex(@"\d{2,5}"); Match match = regex.Match("12345"); Console.WriteLine(match.Value);//"12345" regex = new Regex(@"\d{2,5}?"); match = regex.Match("12345"); Console.WriteLine(match.Value);//"12" match = match.NextMatch(); Console.WriteLine(match.Value);//"34"
分组括号暂时介绍这么多,谢谢!