正则表达式(Regular Expression)是处理文本信息的强大工具,其应用核心是根据模式(pattern)匹配文本内容,然后,可以获取匹配的内容,并进行替换、删除、分割等操作。本文将介绍C#与.NET Framework中的正则表达式应用,并介绍常用格式的判断和处理,如手机号码、18位身份证号码等。
正则表达式的应用基础是模式的定义,在C#中,模式使用字符串定义,由于模式中会使用转义,一般应使用逐字字符串定义模式。定义模式时会使用有着特殊含义的元字符,就像编程语言的关键字一样。下面是定义正则表达式模式时使用的元字符。
在模式中需要匹配这些元字符时,需要使用\字符转义,如\\表示\字符、\.表示圆点等。此外,对于一些特殊的字符,也需要使用\字符转义,如:
首先了解模式的定义,包括匹配内容、匹配次数和匹配位置等。
模式中定义匹配的内容时,主要包括以下一些方法:
除了匹配内容,还可以指定匹配的次数,如:
常用的位置匹配方法如下:
Regex类定义在System.Text.RegularExpressions命名空间,首先来看IsMatch()静态方法的应用,如下面的代码。
using System; using System.Text.RegularExpressions; namespace csfx_demo { class Program { static void Main(string[] args) { string s = "abc"; string p = @"[yn]"; Console.WriteLine(Regex.IsMatch(s, p)); // False } } }
代码中,Regex.IsMatch()方法的第一个参数为字符串;第二个参数定义模式,本例定义为y或n字符,在s字符串中并不包含,方法返回false。可以在s字符串中添加y或n字符来观察执行结果。
默认情况下,模式匹配是区分字母大小写的,即代码中的[yn]并不会匹配Y和N字符,如果需要忽略字母大小写,可以使用Regex.IsMatch()方法的第三个参数,如下面的代码。
using System; using System.Text.RegularExpressions; namespace csfx_demo { class Program { static void Main(string[] args) { string s = "abcXYZ"; string p = @"[yn]"; Console.WriteLine(Regex.IsMatch(s, p, RegexOptions.IgnoreCase)); // True } } }
代码中,使用RegexOptions.IgnoreCase选项,[yn]可以匹配y、n、Y和N。Regex.IsMatch()方法还可以使用第四个参数指定匹配执行的超时时间,类型为TimeSpan。
RegexOptions枚举中包含的选项如下表。
同时使用多个选项时可以使用|运算符,具体的操作原理已在整数运算中讨论过。
Regex类可以使用模式作为构造函数的参数创建对象,然后使用Match()方法可以返回匹配的第一个文本内容,常用版本为,参数一指定字符串,参数二指定RegexOptions参数,不指定时使用默认操作。下面的代码演示了相关应用。
using System; using System.Text.RegularExpressions; namespace csfx_demo { class Program { static void Main(string[] args) { Regex re = new Regex(@"a+"); Match m = re.Match("aaa"); Console.WriteLine(m.Value); // aaa } } }
代码中使用了模式"a+",表示1个或多个a,默认使用贪婪模式,即匹配尽可能多的内容,执行代码会显示aaa。
获取全部匹配内容时使用Matches()方法,参数与Matches()方法相似,如下面的代码演示了非贪婪模式的应用。
using System; using System.Text.RegularExpressions; namespace csfx_demo { class Program { static void Main(string[] args) { Regex re = new Regex(@"a+?"); foreach(Match m in re.Matches("aaa")) Console.WriteLine(m.Value); } } }
本例使用非贪婪模式,当匹配到一个a时即停止本次匹配,并进行下一次匹配,匹配结果分别是三个a。
此外,Regex类的Match()和Matches()方法都有静态版本,常用版本的参数设置为:第一个参数设置字符串,第二个参数设置模式,第三个参数设置RegexOptions选项(可选参数)。
Regex类的Replace()方法可通过模式匹配替换内容,如下面的代码。
using System; using System.Text.RegularExpressions; namespace csfx_demo { class Program { static void Main(string[] args) { string s = "abcaaaxy"; Console.WriteLine(Regex.Replace(s, @"a+", "*")); } } }
代码中,会将s字符串中的连续的1个或多个a替换为一个*符号,执行结果会显示"*bc*xy"。
Replace()方法中,将替换内容设置为空字符串即可完成匹配内容的删除操作,如下面的代码。
using System; using System.Text.RegularExpressions; namespace csfx_demo { class Program { static void Main(string[] args) { Regex re = new Regex(@"a+"); string s = "abcaaabc"; Console.WriteLine(re.Replace(s, "")); } } }
执行代码会显示"bcbc"。
Regex类的Split()方法可以将字符串按模式分割为字符串数组,如下面的代码。
using System; using System.Text.RegularExpressions; namespace csfx_demo { class Program { static void Main(string[] args) { string s = "abcaaAxy"; string[] arr = Regex.Split(s, @"a+", RegexOptions.IgnoreCase); foreach(string e in arr) { Console.WriteLine(e); } } } }
代码中,通过一个或多个a分割字符串,并忽略字母大小写。分割结果为三个元素,第一个元素为第一个a前的空字符串,第二个元素为bc,第三个元素为xy,执行代码会分行显示这三个元素。
开发中,如果在分割结果中不包含空字符串元素,可以对操作进行封装,如下面的代码(cfx/Str.cs)。
using System; using System.Collections.Generic; using System.Text.RegularExpressions; namespace cfx { public static class Str { // 其它代码 // 符合指定的正则表达式模式 public static bool ReMatch(this string s, string pattern, bool ignoreCase = false) { if (ignoreCase) return Regex.IsMatch(s, pattern, RegexOptions.IgnoreCase); else return Regex.IsMatch(s, pattern); } // public static List<string> ReSplit(this string s, string pattern, bool ignoreCase = false) { string[] arr = ignoreCase ? Regex.Split(s, pattern, RegexOptions.IgnoreCase) : Regex.Split(s, pattern); List<string> lst = new List<string>(); foreach (string e in arr) { string a = e.Trim(); if (a.Length > 0) lst.Add(a); } return lst; } } }
代码中,在Str类中定义了string类型的两个扩展方法,包括:
下面的代码,在Program.cs文件中测试这两个扩展方法的应用。
using System; using System.Collections.Generic; using cfx; namespace csfx_demo { class Program { static void Main(string[] args) { string p = @"a+"; string s = "abcaaAxy"; // Console.WriteLine(s.ReMatch(p)); // True // List<string> lst = s.ReSplit(p,true); foreach (string e in lst) Console.WriteLine(e); } } }
第一个输出,s字符串中匹配字母a,显示为True。接下来,对字符串使用一个或多个a分割,分割后列表元素为bc和xy。
下面的代码(cfx/Str.cs),会在Str类中添加IsMobilePhone()方法,判断字符串是否为11位手机号码。
using System; using System.Collections.Generic; using System.Text.RegularExpressions; namespace cfx { public static class Str { // 其它代码 // 判断是否为11位手机号 public static bool IsMobilePhone(this string s) { return ReMatch(s, @"^1\d{10}$"); } } }
本例,定义的模式以数字1开始(^1),以10位数字结束(\d{10}$)。下面的代码,在Program.cs文件测试此方法的应用,可以修改s字符串的内容观察执行结果。
using System; using cfx; namespace csfx_demo { class Program { static void Main(string[] args) { string s = "12345678910"; // Console.WriteLine(s.IsMobilePhone()); // True } } }
18位身份证号码中,前六位是省、市、县三级编码各两位,然后是出生年(4位)、月(2位)、日(2位),接下来是三位序号(奇数为男,偶数为女),最后一位是通过前17位计算出的校验码(0到9的数字或大写字母X)。下面的代码(cfx/Str.cs),在Str类中封装IsIdCard()方法,其功能是判断字符串中的内容是否满足18位身份证号码的基本规则。
using System; using System.Collections.Generic; using System.Text.RegularExpressions; namespace cfx { public static class Str { // 其它代码 } // 是否满足18位身份证号码的基本规则 public static bool IsIdCard(this string s) { // 判断基本模式 string p = @"^[1-9][0-9]{5}[12][0-9]{3}[01][0-9][0-3][0-9]{4}[0-9X]$"; if (ReMatch(s, p) == false) return false; // 计算校验码 // 前17位对应的系数 int[] factor = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 }; // int sum = 0; for(int i = 0; i < factor.Length; i++) { sum = sum + (ToInt(s.Substring(i, 1)) * factor[i]); } // 求除以11的余数 int rem = sum % 11; string chkCode = "10X98765432"; return s.Substring(17) == chkCode.Substring(rem, 1); } } }
代码中,首先使用"^[1-9][0-9]{5}[12][0-9]{3}[01][0-9][0-3][0-9]{4}[0-9X]$"模式判断基本规则,具体含义如下:
然后,会通过前17位数字计算校验码,分别使用前17位各位数字乘以对应的系数,并求和。接下来,使用求得的和除以11,取余数,结果应在0到10之间;chkCode字符串中包含了各个余数值对应的正确的校验码。最后,读取第18个字符(索引17)与正确的校验码对比,相同时返回true,否则返回false。
下面的代码,在Program.cs文件中测试Str.IsIdCard()方法的应用,可以修改s字符串的内容观察执行结果。
using System; using cfx; namespace csfx_demo { class Program { static void Main(string[] args) { string s = "请输入身份证号码"; // Console.WriteLine(s.IsIdCard()); } } }