正则表达式总结
一、引言
在转Java之前对正则表达式只是有一些了解,但一直都没有机会去尝试;后来转了Java想着一切重新来过于是买了本正则表达式必知必会,但是也是偶尔翻翻而已,只会写一些.* .+之类的。
真正开始认真学习正则表达式还是因为之前公司要求弄懂子公司一个大神写的WCS系统;不得不说因为这次机会自己收获了很多,这个系统涉及的知识从多线程、正则表达式、多种设计模式等都有涵盖,自己也因为这次被上司强逼要弄懂以便将WCS部分移植到公司的框架里从而锻炼了分析框架的能力,这为个人后来敢于去分析SpringMVC、mybatis等框架源码打下了心理上和“手感上”的基础;
言归正传,在这次框架分析中,一方面是源码内部用了很多正则表达式,一方面是这个是成熟的WCS系统,实施的时候只需要配置一些配置文件即可,因而有很多正则表达式的需求,这些都促使我去搞懂正则表达式。现在已经用正则表达式几个月了,是时候要好好总结一下了。
二、正则表达式基础
一些常用基础可以参考:http://www.runoob.com/regexp/regexp-syntax.html,和正则表达式必知必会;
1、匹配非换行符
在正则表达式里用字符.表示匹配一个非\r和\n的字符,如字符串a就能被.所匹配;
2、匹配标识符字符
这里的标识符字符是个人为了方便记忆取的,它是转义字符\w,表示匹配一个数字、字母或下划线;其反义则是用大写\W表示,当然也可以[^\w]实现;
3、匹配数字
用\d,匹配单个非数字\D
4、匹配空白符
用\s,匹配非空白符用\S,这里的空白符有空格、\n、\r、\t、\f等等;
5、匹配区间
用[]扩起来的表示一个区间,如[ab]即匹配a或b,还可以用-表示范围,如[0-9]表示匹配单个符合0到9数字的单个字符;[^ab] 表示匹配非a和非b的单个字符,^在[]是取非的意思,如:
content:uman3 上面将会逐个匹配u、m、n、3;
6、重复匹配
匹配一个或多个字符,?,[^ab]?表示匹配0或1个符合[^ab]条件的字符,这里的0个表示是0宽字符,也叫位置,如
content:abm
在上面的字符串中共有4个位置/0宽字符,即a前面、ab中间、bm中间、m后面
而匹配一个或多个是用+,如a+表示至少匹配1个,或连续匹配N个a;而a*则是匹配0个或N个;这里有个概念是贪婪匹配和非贪婪匹配;贪婪匹配顾名思义就是
尽可能的匹配多,而非贪婪匹配则是在符合条件的前提下尽可能匹配少,这个概念只适合于那种匹配个数不定的匹配方式,如+和*以及后面的{m,n}、{m,}等等;
默认情况下是贪婪模式的,举个例子:
content:aaa3 那么pattern是a+的话则进行匹配搜索匹配出的字符串是aaa; 而如果a+?则匹配出的是单个a;要将贪婪模式转换为非贪婪模式在后面加个?即可
匹配一个区间:a{1,3}表示匹配连续的a至少一个最多三个,由于默认是贪婪模式,故如果是连续的a则会取1-3里最大的个数;
匹配一个区间:a{1,}表示匹配连续的a至少1个,最多不限;
匹配固定个数:a{3}表示匹配连续的三个a字符;
三、正则表达式进阶
1、边界匹配
或说匹配符合条件的位置,举例
content:aa mmaauuaa- 则pattern:a{2}\b只会匹配前面的aa和最后的aa,中间的aa不匹配
注意,\b是可以用在pattern的任意位置的,它指\w和\W直接的位置;
2、行边界
有两个字符用来表示行的开头和行的结尾,分别是^和$,如当有多行数据时,这个可以用来保证匹配到的字符串是一行的开头和结尾,当然也可以只要求行开头或行结束;
3、子表达式
用()扩起来的是字表达式,在Java正则表达式里有个group的概念,其实就是对应了不同的子表达式,这个是后面回溯引用的基础,且在替换时保留匹配的字符串的部分内容是很有用的;这里要声明一下(?:pattern)、(?=pattern)、(?!pattern)、(?<=pattern)、(?<!pattern)不属于子表达式,或说它们不是常规的子表达式不能用于回溯引用。
4、回溯引用
即正则表达式的pattern里可以引用前面的子表达式,举个例子
content:<h2>aaa mm</h3> pattern:<[hH>[1-6]>.*</[hH][1-6]>那么这个pattern是能够匹配上面的content的,但是上面的标签显然是不合规定的,这个时候通过回溯引用就能够保证前后的一致性:pattern:<[hH]([1-6])>.*</[hH]\1>,这里的\1就是一个回溯引用,它指向第一个用()括起来的子表达式
5.向前查找和向后查找
无论是向前查找还是向后查找都是查找的是位置或说零宽字符,向前查找分为普通向前查找和取非向前查找,分别用(?=pattern)和(?!pattern)表示;
向前查找在进行一些特殊匹配时十分有用,比如要匹配包含数字字母小写和字母大写的长度为12-16的密码
pattern:^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,16}$ 这里解释一下(?=.*\d)表示匹配一个位置,这个位置向前查找.*个字符后有字符\d,从而规定匹配的字符串必须包括数字
对于向前查找取非则是要理解为匹配一个位置,该位置之后pattern所有的情形都满足条件。
向后查找则用的较少,主要是只有部分语言支持,且有很大限制,比如pattern不能有条件,只能是固定的情况;
正则表达式分组:(注意这里之前自己一直记错了,?=和?!对应而不是?!=,?!=是错误的)
其中除了(?:aaa|bbb)这种是只匹配不捕获,其他的如(.*)【分组匹配捕获且选中】或(?=xxx)或(?!xxx)都是会捕获的(不过后面两个应该叫分组匹配捕获但不选中)
这里捕获的意思是它会占用一个组如替换时$n这种形式能匹配到(?=和?!都是匹配,不过一个是正向匹配,一个是取反的意思,如(?!元)表示匹配后面是元的所有相反的情况,而(?!.*a)表示匹配所有后面符合.*a的相反的情况)
(?=.*a)是肯定 预查 ,表示后面肯定存在一个以上符合.*a的情形,而(?!.*a)则是否定 预查 ,表示后面一定不存在符合.*a的字符串;
以后自己叫(?=xxx)是正向肯定预查(正向是说往后查的意思),而(?!xxx)是正向否定预查;
而反向XX预查很多语言不支持,即便支持也不支持那种动态的字符;
反向肯定预查是在左边写(?<=xxx),反向否定预查是(?<!xxx)
posted on 2018-03-18 11:18 Silentdoer 阅读(169) 评论(0) 编辑 收藏 举报