本文将从正则表达式的概念、语法、常用方法等方面进行详细介绍,力求在一篇文章中囊括正则表达式应知应会内容。
那么什么是正则表达式,它产生的原因是什么,我们在什么样的场景下使用它,如何使用,下面将一一为大家解答。
一、 正则表达式
在1951 年,一位名叫Stephen Kleene的数学科学家,发表了一篇题目是《神经网事件的表示法》的论文,利用称之为正则集合的数学符号来描述此模型,引入了正则表达式的概念。正则表达式被作为用来描述其称之为“正则集的代数”的一种表达式,因而采用了“正则表达式”这个术语。这正是正则表达式的起源,之后一段时间,人们发现可以将这一工作成果应用于其他方面。自此以后,正则表达式被广泛地应用到各种UNIX或类似于UNIX的工具中,如大家熟知的Perl。
那么,什么是正则表达式?简单的说,正则表达式是一些用来匹配和处理文本的字符串。正则表达式是用正则表达式语言创建的,正则表达式语言也有特殊的语法和指令。在使用正则表达式时,你会发现几乎所有的问题都不止一个答案。正则表达式主要的用途有两个:一个是进行搜索,另一个是用于替换。
二、 常用语法
1、匹配任意字符
在正则表达式里,.字符(英文句号)可以匹配任何一个单个的字符,包括字母、数字甚至.本身。例如图1所示:
图1 任意字符的匹配
2、匹配一组字符
[]可以定义一个字符集合,在使用[]定义的字符集合里,这两个元字符之间的所有字符都是该集合的组成部分,字符集合的匹配结果是能够与该集合里的任意一个成员相匹配的文本。如图2所示,我只想找出名为sa或ma的后缀名为doc的文件,就可以使用[sm]a.\.doc正则表达式进行查找。这里\是转义字符,那什么是转义字符?正则表达式中有一些有着特殊含义的字符,比如英文句号(.)用来匹配任何一个单个字符,这样的字符被称作元字符。因为元字符有特殊的含义,所以这些字符就无法代表它们本身,因此,在正则表达式中用转义字符\来对元字符进行转义,使得元字符能够匹配自身,元字符详情见附表1.
图2匹配一组字符
3、取非匹配
取非匹配就是除了那个字符集合里的字符,其他字符都可以匹配。用元字符^表示,如图三所示。[ms]a[^0-9]\.doc表示只匹配ma或sa开头的,后面是非数字符合的,后缀名是doc的字符串。
图3 取非匹配
4、匹配数字与非数字
我们知道[0-9]可以用来匹配任何一个数字,[^0-9]可以匹配任何一个非数字字符,在正则表达式中有两个元字符也可以达到相同的目的。如图4所示,可见与图3答案一致。
\d 任何一个数字字符(等价于[0-9])
\D 任何一个非数字字符(等价于[^0-9])
图4 元字符匹配非数字字符
5、匹配字母和数字(与非字母和数字)
\w 匹配任何一个字母数字字符(大小写均可)或下划线字符,等价于[a-zA-Z0-9_].
\W 匹配任何一个非字母数字或非下划线字符,等价于[^a-zA-Z0-9_].
6、匹配一个或多个字符
要想匹配同一个字符(或字符集合)的多次重复,只需在这个字符(或字符集合)加上一个+字符后缀即可。比如,a匹配a本身,a+将匹配一个或多个连续出现的a。如图5所示。http://匹配本身,[\w./] 匹配任何一个字母数字字符(大小写均可)或下划线字符和/,+表示重复
图5匹配一个或多个字符
7、匹配零个或多个字符
+匹配一个或多个字符,但不匹配零个字符,如果要想匹配一个出现零次或多次的字符,可以用元字符*来表示。如图6所示,在测试框中增加http://www.baidu.com,并将上面的+字符换成*字符,可见+与*的区别。如图6所示
图6匹配零个或多个字符
8、匹配零个或一个字符
元字符?可以匹配一个字符的零次或一次出现,最多不能超过一次。在上面这个例子中我们只匹配到了http,如果既要匹配http又要匹配https,?无疑是一个非常好的选择。如图7所示
图7匹配零个或一个字符
9、匹配的重复次数
如果你想为重复匹配次数设定一个精确的指,把重复次数写在{}中间即可,{3}表示前一个字符或字符集必须在原始文本里连续重复出现3次才算是一个匹配,如果只出现了两次,则不算是一个匹配。{}语法还可以用来为重复匹配次数设定一个区间,比如{2,4}的含义是最少重复2次,最多重复4次。{3,}表示最少重复3次。如图8所示。\d{4,}[-]\d{1,2}[-]\d{1,}只能匹配xxxx-xx-xx的日期格式,不能区别错误的日期,如2018-19-36.
图8匹配的重复次数
10、懒惰型元字符
看下这个例子,<B>mk</B> and <B>EU</B>,我想把两个<B>标签里的文本匹配出来,如果使用正则表达式<B>.*</B>,则会出现图9所示结果。
图9
可以看到,这个结果并不是我们想要的结果,这是因为*属于“贪婪型”元字符,它在匹配时的行为模式是多多益善而不是适可而止。使用它的懒惰型版本即可解决问题,如图10所示。<B>.*?</B>
图10 懒惰型版本
贪婪型元字符 | 懒惰型元字符 |
* | *? |
+ | +? |
{n,} | {n,}? |
11、位置匹配(单词边界)
\b用来匹配一个单词的开始或结尾。\b匹配的是一个位置,这个位置位于一个能够用来构成单词的字符和一个不能用来构成单词的字符之间。例如,我只想匹配cat这个单词,如果直接使用cat字符,则将匹配所有包含cat字符的单词(如图11所示),使用\bcat\b,则将只定位到cat一个单词(如图12所示)。
图11 位置匹配1
图12 位置匹配2
12、字符串边界
用来定义字符串边界的元字符有两个:一个是用来定义字符串开头的^,另一个是用来定义字符串结尾的$。如图13所示,^\s*将匹配一个字符串的开头位置和随后的零个或多个空白字符。
图13 字符串开头
13 子表达式
正则表达式中子表达式是指,把一个表达式作为一个独立元素来使用,子表达式使用元字符()来表示。192.168.5.46,如果要匹配这个IP地址,可以用\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}来表示。但是这里\d{1,3}\.重复了3次,则可以使用子表达式(\d{1,3}\.)来表示,写成(\d{1,3}\.){3}\d{1,3},如图14所示。
图14 子表达式
14、子表达式的嵌套
我们来考虑这样一个正则表达式,匹配合法的IP地址。一个合法的IP地址将满足以下条件:
- 任何一个1位或2位数字
- 任何一个以1开头的3位数字
- 任何一个以2开头、第2位数字在0-4之间的3位数字
- 任何一个以25开头、第3位数字在0-5之间的3位数字
综上,可以编写出(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))的正则表达式,效果如图15所示。
图15 子表达式的嵌套
15、回溯引用匹配
回溯引用允许正则表达式模式引用前面的匹配结果,举个例子,我们想找到文本中连续重复的单词,如图16所示,我们可以构造如下的正则表达式:[]+(\w+)[]+\1,这里[]+匹配一个或多个空格,\w+匹配一个或多个数字字符,[]+匹配随后的空格,注意这里\w+是加括号的,表示一个子表达式,\1就是一个回溯引用,而它引用的就是前面加括号中的内容。
图16回溯引用匹配
16、向前/后查找
向前查找指定了一个必须匹配但不在结果中返回的模式。向前查找其实也是一个子表达式,以?=开头,需要匹配的文本跟在=后面。有些正则表达式文档使用术语“消费”来表述“匹配和返回文本”的含义。在向前查找里,被匹配的文本不包含在最终返回的匹配结果里,这里称为“不消费”。如图17所示。.+匹配任意文本,子表达式(?=:)匹配:,注意匹配的:并没有出现在最终匹配的结果里,我们用?=:表明只要找到:就可以,不需要表示出来,也就是“不消费”。同理,我们用?<=表示向后查找。
图17 向前查找
三、 常见的正则表达式
1、匹配中文字符:[\u4e00-\u9fa5]
2、匹配邮件地址:[\w!#$%&'*+/=?^_`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[\w](?:[\w-]*[\w])?
3、匹配网址URL:[a-zA-z]+://[^\s]*
4、匹配国内电话号码:\d{3}-\d{8}|\d{4}-\{7,8}
5、匹配18位身份证号:^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$
6、匹配(年-月-日)格式日期:([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8])))
7、手机号码:^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\\d{8}$
8、银行卡号:^(\d{16}|\d{19})$
四、附表
元字符 | 说明 |
. | 匹配任意单个字符 |
| | 逻辑或操作符 |
[] | 匹配字符集合中的一个字符 |
[^] | 对字符集合求非 |
- | 定义一个区间(例如[A-Z]) |
\ | 对下一个字符转义 |
* | 匹配前一个字符(子表达式)的零次或多次重复 |
*? | *的懒惰型版本 |
+ | 匹配前一个字符(子表达式)的一次或多次重复 |
+? | +的懒惰型版本 |
? | 匹配前一个字符(子表达式)的零次或一次重复 |
{n} | 匹配前一个字符(子表达式)的n次重复 |
元字符 | 说明 |
{m,n} | 匹配前一个字符(子表达式)至少m次且至多n次重复 |
{n,} | 匹配前一个字符(子表达式)n次或更多次重复 |
{n,}? | {n,}的懒惰型版本 |
^ | 匹配字符串的开头 |
\A | 匹配字符串的开头 |
$ | 匹配字符串的结束 |
\Z | 匹配字符串的结束 |
\< | 匹配单词的开头 |
\> | 匹配单词的结束 |
\b | 匹配单词的边界(开头和结束) |
\B | \b的反义 |
[\b] | 退格字符 |
\c | 匹配一个控制字符 |
\d | 匹配任意数字字符 |
\D | \d的反义 |
\f | 换页符 |
\n | 换行符 |
\r | 回车符 |
\s | 匹配一个空白字符 |
\S | \s的反义 |
\t | 制表符 |
\v | 垂直制表符 |
\w | 匹配任意字母数字字符或下划线字符 |
\W | \w的反义 |
\x | 匹配一个十六进制数字 |
\0 | 匹配一个八进制数字 |
() | 定义一个子表达式 |
\1 | 匹配第一个子表达式 |
?= | 向前查找 |
?<= | 向后查找 |
?! | 负向前查找 |
?<! | 负向后查找 |
?() | 条件(if then) |
?()| | 条件(if then else) |
\E | 结束\L或\U转换 |
\l | 把下一个字符转换为小写 |
\L | 把后面的字符转换为小写,直到遇见\E为止 |
\u | 把下一个字符转换为大写 |
\U | 把后面的字符转换为大写,直到遇见\E为止 |
(?m) | 分行匹配模式 |