我所知道的正则表达式(1)- 基础知识
本文作为温故系列第一篇,主要帮助我回忆正则表达式的基础知识。
正则表达式是处理文本时的有力工具,能够按照一定的模式或规则去匹配具有一定格式的文本,并可以完成分组、替换等复杂功能。作为一程序员,正则作为必备武器,能给我们处理文带来极大的便利。经常用到的正则的场景如:javascript验证输入格式(密码、电话、email等)、网页爬虫从网页中的解析出需要的内容,替换相同模式的文本内容等。
正则中预定义了一些元字符,用来表示字符、个数、重复次数、逻辑关系等,以下是大致介绍:
字符匹配
. | 匹配任意一个字符 |
^ | 行开头 |
$ | 行结束 |
\ | 转义符 |
\d | 一个数字,相当于[0-9] |
\s | 一个空白字符,相当于[ \t\n\x0B\f\r] |
\w | 一个单词中的字符:字母、数字、下划线,相当于[a-zA-Z_0-9] |
\b | 单词的边界(单词的开头或结尾) |
[] | []里面的任意一个字符 |
数量匹配,用来匹配字符出现的次数
x* | 0个或者任意个x, |
x? | 0个或这1个x |
x+ | 至少1个x |
x{n} | n个x |
x{n,} | 至少n个x |
x{n,m} | 至少n个x,至多m个x |
注:此为贪婪模式,就是匹配最多的结果。例如有个字符串abcdefg#aaa, [a-z]+的第一次匹配结果为abcdefg,不会是a或者ab等
[]的使用举例
[0-9] | 一个数字 |
[^0-9] | 一个非数字字符 |
[a-z] | a-z中的任意一个字母(所有小写字母) |
[A-Z] | A-Z中的任意一个字母(所有大写字母) |
[a-zA-Z] | 所有字母的一个 |
[a-zA-Z0-9] | 所有字母和数字中的一个 |
[a-ce-z] | a-c和e-z中的一个,不含d |
[a-d[m-p]] | a- d或者m-p中的一个,这个不是所有解释器都支持 |
注:在[]中的^不是表示行开头,而是标识反义,意为“除xxx之外的所有”
逻辑关系
XY | X后面是Y |
X|Y | X或者Y |
分组
在一个正则表达式中,将局部用()包含成子表达式,目的是对子表达式进行重复匹配等操作,每个()就是一个组。在匹配的过程中,会将匹配每个子表达式字符按顺序保存在临时变量中,最多可保存9个分组,可以使用1到9来引用。
例如有这样文本,要求匹配出网站名称和链接:
<a href=”http://www.google.com”>谷歌</a><a href=”http://www.baidu.com”>百度</a>
就可以使用分组,正则为:<a href=”([^\"]+)”>([^<]+)</a> ,每个括号为一组,共2个分组。
看下面的Java的测试用例:
|
|
---|
最终输出:
谷歌 – http://www.google.com
百度 – http://www.baidu.com
分组替换
分组可以很方便的完成替换,例如在apache的urlrewrite模块中,经常用到如下的正则:
RewriteRule ^/article/([0-9]+)$ /article.php?id=$1 [L]
就是通过分组1获取到id,从而通过$1赋值到后面的url中去
不捕获分组(2011-12-16添加)
(?:group-regex) | 按分组(group-regex)来匹配,但不捕获该分组 |
(?<name>group-regex) | 命名分组为name, 可以通过此名字捕获该分组 |
我所知道的正则表达式(2)- 贪婪模式与懒惰模式
前段时间对自己以前了解的正则表达式的基础知识做了一个总结,有了基础知识已经能够能应付大多数情况,像字符串匹配、正则替换、校验等;但是再使用的过程中你可能发现一个问题,就是当使用重复元字符匹配数量时,总是会尽可能长的去匹配,而有时这恰恰不是你想要的。看下面的例子:
<a href=”http://www.google.com”>谷歌</a><a href=”http://www.baidu.com”>百度</a>
你希望匹配出每个链接的html内容。如果你用 <a (.*)</a>会首先匹配到整个字符串,而不是你希望的<a href=”http://www.google.com”>谷歌</a>和<a href=”http://www.baidu.com”>百度</a>。
为什么会出现这种情况呢?这就是正则表达式的贪婪模式。当出现重复数量的时候,会尽可能的多匹配。上述的正则表达式中 . 表示任意字符,* 代表可以重复出现任意个,根据正则表达式的贪婪个性,不匹配到最后才怪呢。就像人一样,有贪婪就有懒惰,一个?就可以让正则立刻改变本性,这时的正则就表现懒惰模式的本性了。
修改后的正则表达式为:<a (.*?)</a>,这时就可以匹配出每个连接的html了。
测试用例为:
|
|
---|
最后总结一下数量元字符懒惰模式的常用写法(其实就是多了一个?):
x*? | 0个或者任意个x, 最少匹配 |
x?? | 0个或这1个x, 最少匹配 |
x+? | 至少1个x, 最少匹配 |
x{n,}? | 至少n个x, 最少匹配 |
x{n,m}? | 至少n个x,至多m个x, 最少匹配 |
我所知道的正则表达式(3)- 零宽断言
前段时间总结了正则表达式的基础知识以及贪婪模式和懒惰模式,今天再总结一下零宽断言。
正则表达式里面比较高级的应用就属于零宽断言了。那么什么是零宽断言呢?拆分法从字面上分析一下,零宽,即宽带为0,意味者不会返回匹配的字符,以为匹配的是当前字符的位置。断言,就是预言、假设,意味着从此处假设存在什么情况。那么零宽断言的意思就是假定从此位置开始满足某种情况。
根据断言字符串位于当前位置的前后关系,分为正向和反向断言,根据断言肯定和否定的语气,又有正向否定断言和反向否定断言。肯定即断言存在该字符串、否定即相反的意思:存在的不是该字符串,总之概念比较绕口,下表介绍的时候顺便给出英文:
(?=X) | 正向断言,假定该位置后跟的是X zero-width positive lookahead |
(?!X) | 正向否定断言,假设该位置后跟的不是X zero-width negative lookahead |
(?<=X) | 反向断言,假设该位置前跟的是X zero-width positive lookbehind |
(?<!X) | 反向否定断言,假设该位置前跟的不是X zero-width negative lookbehind |
举例:
(?=X) 正向断言
[^\s]+?(?=ing) 来匹配 having doing listing,会匹配出 hav, do, list,注意:并不会匹配出ing,因为ing是零宽断言的部分。
(?!X) 正向否定断言
这个可参考以前的文章:一个匹配数字和字母密码的正则表达式
(?<=X) 反向断言
(?<=hell)[a-z]+ 来匹配test hellen hellas helloween,会匹配出 en, as, oween
(?<!X) 反向否定断言
[a-z]+(?<!hell)en 来匹配testen hellen hellas helloween,会匹配出testen和helloween
注:所有的案例都在UE下进行测试。
一个匹配数字和字母密码的正则表达式
一个用户注册功能的密码有如下要求:由数字和字母组成,并且要同时含有数字和字母,且长度要在8-16位之间。
如何分析需求?拆分!这就是软件设计的一般思路了。于是乎,拆分需求如下:
1,不能全部是数字
2,不能全部是字母
3,必须是数字或字母
只要能同时满足上面3个要求就可以了,写出来如下:
|
|
---|
分开来注释一下:
^ 匹配一行的开头位置
(?![0-9]+$) 预测该位置后面不全是数字
(?![a-zA-Z]+$) 预测该位置后面不全是字母
[0-9A-Za-z] {8,16} 由8-16位数字或这字母组成
$ 匹配行结尾位置
注:(?!xxxx) 是正则表达式的负向零宽断言一种形式,标识预该位置后不是xxxx字符。
测试用例如下:
|
|
---|