目前为止,许多编程语言和工具都包含对正则表达式的支持,C#也不例外,C#基础类库中包含有一个命名空间(System.Text.RegularExpressions)和一系列能够充分发挥规则表达式威力的类(Regex、Match、Group等)。那么,什么是正则表达式,怎么定义正则表达式呢?正则表达式
1、正则表达式基础编程
什么是正则表达式编程语言
在编写字符串的处理程序时,常常会有查找符合某些复杂规则的字符串的须要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。函数
一般,咱们在使用WINDOWS查找文件时,会使用通配符(*和?)。若是你想查找某个目录下的全部Word文档时,你就可使用*.doc进行查找,在这里,*就被解释为任意字符串。和通配符相似,正则表达式也是用来进行文本匹配的工具,只不过比起通配符,它能更精确地描述你的需求——固然,代价就是更复杂。工具
一个简单的例子——验证电话号码学习
学习正则表达式的最好方法是从例子开始,下面咱们从验证电话号码开始,一步一步的了解正则表达式。spa
在咱们国家,电话号码(如:0379-65624150)一般包含3到4为以0开头的区号和一个7或8为的号码,中间一般以连字符’-’隔开。在这个例子中,首先咱们要介绍一个元字符\d,它用来匹配一个0到9的数字。这个正则表达式能够写成:^0\d{2,3}-\d{7,8}$对象
咱们来对他进行分析,0匹配数字“0”,\d匹配一个数字,{2,3}表示重复2到3次,-只匹配”-”自身,接下来的\d一样匹配一个数字,而 {7,8}则表示重复7到8次。固然,电话号码还能够写成 (0379)65624150,这里就交给读者完成。ci
A. 元字符文档
在上面的例子中,咱们接触到了一个元字符\d,正如你所想的,正则表达式还有不少像\d同样的元字符,下表列出了一些经常使用的元字符:
元字符 |
说明 |
. |
匹配除换行符之外的任意字符 |
\b |
匹配单词的开始或结束 |
\d |
匹配数字 |
\s |
匹配任意的空白符 |
\w |
匹配字母或数字或下划线或汉字 |
^ |
匹配字符串的开始 |
$ |
匹配字符串的结束 |
表一、经常使用的元字符
B. 转义字符
若是你想查找元字符自己的话,好比你查找.,或者*,就出现了问题:你没办法指定它们,由于它们会被解释成别的意思。这时你就得使用\来取消这些字符的特殊意义。所以,你应该使用\.和\*。固然,要查找\自己,你也得用\\.
例如:unibetter\.com匹配unibetter.com,C:\\Windows匹配C:\Windows。
C. 限定符
限定符又叫重复描述字符,表示一个字符要出现的次数。好比咱们在匹配电话号码时使用的{3,4}就表示出现3到4次。经常使用的限定符有:
限定符 |
说明 |
* |
重复零次或更屡次 |
+ |
重复一次或更屡次 |
? |
重复零次或一次 |
{n} |
重复n次 |
{n,} |
重复n次或更屡次 |
{n,m} |
重复n到m次 |
表二、经常使用的限定符
2、.NET中正则表达式的支持
System.Text.RegularExpressions 命名空间包含一些类,这些类提供对 .NET Framework 正则表达式引擎的访问。该命名空间提供正则表达式功能,能够从运行在 Microsoft .NET Framework 内的任何平台或语言中使用该功能。
A、在C#中使用正则表达式
在了解了C#中支持正则表达式的类后,咱们一块儿来将上面提到的验证电话号码的正则表达式写入C#代码中,实现电话号码的验证。
第一步,创建一个名为SimpleCheckPhoneNumber的Windows项目。
第二步,引入System.Text.RegularExpressions命名空间。
第三步,写出正则表达式。这里的正则表达式就是上面的验证号码的字符串。因为上面的字符串只能验证用连字符链接区号和号码的方式的电话号码,因此咱们作了一些修改:0\d{2,3}-\d{7,8}|\(0\d{2,3}\)\d{7,8}。在这个表达式中,| 号面的一部分是咱们上面提到过的,后面一部分是用来验证(0379)65624150这种电话号码写法的。因为 ( 和 ) 也是元字符,因此要用转义字符。| 表示分支匹配,要么匹配前面的一部分,要么匹配后面的一部分。
第四步,正则表达式构造一个Regex类。
第五步,使用Regex类的IsMatch方法验证匹配。Regex类的IsMatch()方法返回一个bool值,若是有匹配项,返回true,不然返回false。
3、正则表达式进阶
A. 分组
在匹配电话号码的时候,咱们已经用到太重复单个字符。下面咱们来了解如何使用分组来匹配一个IP地址。
众所周知,IP地址是四段点分十进制的字符串表示的。因此,咱们能够经过地址的分组,来进行匹配。首先,咱们来匹配第一段:2[0-4]\d|25[0-5]|[01]?\d\d? 这段正则表达式能够匹配IP地址的一段数字。2[0-4]\d 匹配以2开头,十位为0到4,个位为任何数字的三位字段,25[0-5] 匹配以25 开头,个位为0到5 的三位字段,[01]?\d\d? 匹配任何以1者0头,个位和十位为任何数子的字段。? 表示出现零次或一次。因此, [01] 和 最后一个 \d 均可以不出现,若是咱们再向这个字符串后面添加一个 \. 来匹配 . 就能够划分一个段了。如今,咱们把 2[0-4]\d|25[0-5]|[01]?\d\d?\. 当作一个分组,就能够写成 (2[0-4]\d|25[0-5]|[01]?\d\d?\.) 。接下来咱们就来使用这个分组。将这个分组重复两次,而后,再使用 2[0-4]\d|25[0-5]|[01]?\d\d? 就能够了。完整的正则表达式为: (2[0-4]\d|25[0-5]|[01]?\d\d?\.){3}2[0-4]\d|25[0-5]|[01]?\d\d?
B.后向引用
在咱们了解分组之后,咱们就可使用后向引用了。所谓后向引用,就是使用前面捕获的结果,对后面的字符进行匹配。多用于匹配重复字符。好比匹配 go go 这样的重复字符。咱们就可使用 (go) \1来进行匹配。
默认状况下,每一个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。固然,你也能够本身指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:(?<Word>\w+)(或者把尖括号换成'也行:(?'Word'\w+)),这样就把\w+的组名指定为Word了。要反向引用这个分组捕获的内容,你可使用\k<Word>,因此上一个例子也能够写成这样:\b(?<Word>\w+)\b\s+\k<Word>\b。
自定义组名还有另一个好处,在咱们的C#程序中,若是须要获得分组的值,咱们就能够很明确的使用咱们定义的分组的名字来获得,而没必要使用下标。
当咱们并不想使用后向引用时,是不须要捕获组记忆任何东西的,这种状况下就能够利用(?:nocapture)语法来主动地告诉正则表达式引擎,不要把圆括号的内容看成捕获组,以便提升效率。
C.零宽断言
在前面的元字符介绍中,咱们已经知道了有这样一类字符,能够匹配一句话的开始、结束(^ $)或者匹配一个单词的开始、结束(\b)。这些元字符只匹配一个位置,指定这个位置知足必定的条件,而不是匹配某些字符,所以,它们被成为 零宽断言。所谓零宽,指的是它们不与任何字符相匹配,而匹配一个位置;所谓断言,指的是一个判断。正则表达式中只有当断言为真时才会继续进行匹配。
在有些时候,咱们精确的匹配一个位置,而不只仅是句子或者单词,这就须要咱们本身写出断言来进行匹配。下面是断言的语法:
断言语法 |
说明 |
(?=pattern) |
前向确定断言,匹配pattern前面的位置 |
(?!pattern) |
前向否认断言,匹配后面不是pattern的位置 |
(?<=pattern) |
后向确定断言,匹配pattern后面的位置 |
(?<!pattern) |
后向否认断言,匹配前面不是pattern的位置 |
表三、断言的语法及说明
很难理解吗?咱们来看一个例子。
有一个标签:<book>,咱们想要获得标签<book>的标签名(book),这个时候,咱们就可使用断言来处理。看下面这个表达式:(?<=\<)(?<tag>\w*)(?=\>) ,使用这个表达式,能够匹配< 和 >之间的字符,也就是这里的book。使用断言还还能够写出更加复杂的表达式,这里就再也不举例了。
还有一点很是重要,就是断言语法所使用的圆括号并不做为捕获组,因此不能使用编号或命名来对它进行引用。
D.贪婪与懒惰
当正则表达式中包含能接受重复的限定符时,一般的行为是(在使整个表达式能获得匹配的前提下)匹配尽量多的字符。来看一下这个表达式:a\w*b ,用它来匹配字符串 aabab 时,获得的匹配结果是 aabab 。这种匹配被称为贪婪匹配。
有些时候,咱们但愿让它尽量的少重复,即用上面的例子获得的匹配结果是 aab,这时咱们就要使用懒惰匹配。懒惰匹配须要在重复限定符的后面添加一个 ? 符号,上面的表达式就能够写成:a\w*?b 咱们再来匹配字符串 aabab时,获得的匹配结果是 aab 和 ab 。
也许这个时候你要问,ab 比aab重复次数更少,为何不先匹配ab呢?其实在正则表达式中还有比贪婪/懒惰优先级更高的规则:最早开始的匹配拥有最高的优先权——The match that begins earliest wins。
E.注释
语法:(?#comment)
例如:2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)
注意:若是使用注释,则须要格外注意不要在注释的小括号前面出现空格、换行符等一些字符,若是能够忽略这些字符,则最好使用“忽略模式里的空白符”选项,即C#中RegexOptions枚举的IgnorePatternWhitespace选项(C#中的RegexOptions枚举下面将会提到)。
F. C#中的处理选项
在C#中,可使用RegexOptions 枚举来选择C#对正则表达式的处理方式。下面是MSDN中RegexOptions 枚举的成员介绍:
C#中Capture类、Group类、Match类
Capture类:表示单个子表达式捕获中的结果。Capture类表示单个成功捕获中的一个子字符串。该类没有公共构造函数,能够从Group类或者Match类中获得一个Capture类的对象集合。Capture类有三个经常使用属性,分别是Index、Length和Value。Index表示捕获的子字符串的第一个字符的位置。Length表示捕获的子字符串的长度,Value表示捕获的子字符串。
Group类:表示正则表达式中分组的信息。该类提供了对分组匹配的正则表达式的支持。该类没有公共构造函数。能够从Match类中获得一个Group类的集合。若是正则表达式中的分组已命名,则可使用名字对其进行访问,若是没有命名,则能够采用下标访问。注意:每个Match的Groups集合中的第0个元素(Groups[0])都是这个Match捕获的字符串,也是Capture的Value。
Match类:表示单个正则表达式匹配的结果。该类一样没有公共构造函数,能够从Regex类的Match()方法获得该类的一个实例,也可使用Regex类的Matches()方法获得给类的一个集合。
这三个类都能表示单个正则表达式匹配的结果,但Match类获得的更为详细,包含捕获和分组信息。因此,Match类在这个三个类中是最经常使用的。