Description
前言
正则表达式的入门不难,看一些例子,试着模仿模仿,大概就可以粗通,并能在工作中解决不少问题。然而事实上,正则表达式并不是每天都会用到,而其密码般的形象,随着时间推移很容易被遗忘,所以开发者对于正则表达式的记忆迅速消退,每次遇到新问题,都要查询资料才能重新唤起记忆。对于稍微复杂一点的问题,只好求助于现成的解决方案。(我想说,我现在就是这样的)反反复复,长期如此,不但应用水平难以明显提升,而且会对这项技术逐渐产生一定的恐惧感和厌烦情绪。
对于正则表达式应用的高级阶段,要求开发者必须充分理解正则表达式的能力范围,能够将一些正则表达式技术组合应用,达成超乎一般想象的效果。而要达到这样的程度,不经过系统的学习是不可能的。
基础
如何理解?
- 从较低的层面上来说:正则表达式描述的是一串文本的特征,我们可以用它来验证用户输入的数据,或者也可以用它来检索大量的文本。
- 从较高的层面上来说:正则表达式容许用户掌控他们自己的数据-控制这些数据,让它们为自己服务。掌握正则表达式就是掌握自己的数据。
正则表达式是一种思想-各种工具以各种方式来实现它。
举例
1. 字符与字符组
比如说搜索单词是"grey",但是又不太确定是否被写作了“gray”,那我们就可以使用正则表达式结构体。如:gr[ea]y
,他表示先找到g,跟着是一个r,然后使一个a或者e,最后是一个y。或者另外一个表达式:sep[ea]r[ea]te
也是同样的道理。对于gr[ea]y
,其中的普通字符,如g和r都是接下来的意思-首先匹配g,接下来匹配r,这与字符组内部的情况是完全相反的。如字符组[ea],它的意思是“或”,即e或者a。
在一个字符组中可以列举任意多个字符,如:[123456]
匹配1到6中的任意一个数字。在字符组内部,字符组源字符表示一个范围,如<H[1-6]>
===<H[123456]>
,所以[0-9]
和[a-z]
是常用的匹配数字和小写字母的简便方式。多重范围也是允许的,如:[A-Fa-f0-9]
,顺序无所谓。
我们还可以随心所欲的吧字符范围与普通文本结合起来:[0-9A-Z_!.?]
能够匹配一个数字、大写字母、下划线、惊叹号、点号或者是问号。
需要注意的是:只有在字符组内部,连字符才是元字符,否则它就只能匹配普通的连字符号。但是如果连字符出现在字符组的开通,他表示的就是一个普通字符,而不是一个范围。同样,问号和点号在字符组中也是普通字符!!!
2. 排除型字符组
规则:用[^.......]
取代[.......]
这个字符组就会匹配任何未列出的字符。如[^1-6]
匹配除了1到6之外的任何字符。但是在字符组外部,^表示一个行锚点,但是在字符组内部,它就是一个元字符。
所以,我们可以记住,排除型字符组表示:匹配一个未列出的字符,而不是“不要匹配列出的字符”。下面看两个例子来体会一下:
上面的例子其实说明了,在“q”之后,还必须跟上一个字符,但是这个字符不能是“u”,如果这个字符组以q结尾,后面没有其他字符了,这个时候它也是不正确的。(一个字符组,即是是排除型字符组,也需要匹配一个字符)
3. 用点号匹配任意字符
元字符点"."是用来匹配任意字符的字符组的简便写法。例如:我们需要搜索03/19/76、03-19-76或者03.19.76,如果不觉得麻烦的话,用一个明确容许"/"、“-”、“.”的字符组来构建正则表达式,如03[-./]19[-./]76
,当然也可以简单的使用03.19.76
因为点号可以匹配任何字符,所以它也能匹配下面的字符:
所以,对于这种匹配更精确的正则表达式是:03[-./]19[-./]76
,另外一种方式更加容易理解但是不够细致。
注意:03[-./]19[-./]76
中的点号并不是元字符,因为它们在字符组内部。这里的连字符同样也不是元字符,因为它们都紧跟在[
或者[^
之后,如果连字符不在字符组的开头,如[.-/]
,这本身就是一种错误的使用方式。
4. 多选解构(匹配任意子表达式)
"|"是一个非常简捷的元字符,它的意思是或(or)。使用它,我们可以把不同的子表达式组合成一个总的表达式,而这个总的表达式又能够匹配任意的子表达式。
例如:gr[ea]y
又可以写成grey|gray
或者是gr(a|e)y
(它使用括号来划定多选结构的范围,正常情况下,括号也是元字符)
多选结构可以包括很多字符,但不能超越括号的界限。另一个例子就是:(Frist|1st).[Ss]treet
进一步还可以化简为(Fir|1)st.[Ss]treet
不过这样依赖会有点难以识别。
在一个包含多选结构的表达式中使用脱字符和美元符的时候要特别小心。比较^From|Subject|Date:.
和^(From|Subject|Date):.
就会发现,虽然它们看起来与之前的Email例子很相似,但用处却大不相同。第一个表达式由3个多选分支构成,所以它能匹配^Form
或Subject
或Date:
中的一个,实用性不大。而我们希望的正是像^(From|Subject|Date):.
这样能够匹配一行的起始位置,然后匹配^Form
Subject
和Date:
中的任意一个,然后匹配:
,所以它能够匹配的文本是:
- 行起始,然后是From,然后是
:.
; - 行起始,然后是Subject,然后是
:.
; - 行起始,然后是Date,然后是
:.
;