目录
写在前面:
工作学习中经常会用到正则表达式,之前总是在网上借鉴别人的正则表达式,拿过来用也看不懂什么意思,却实现了想要的功能!但常在河边走,哪能不湿鞋,为了不湿鞋,专门去学习并总结了正则表达式的用法,希望以后借鉴完别人的正则表达式,能知道所以然吧!如果学完之后能多加练习,自己也能写出有用的正则表达式。
一、正则表达式介绍
1、正则表达式的用途
- 搜索(查找特定的信息)
- 替换(查找并编辑特定的信息)
例如:在一段文本中搜索某一个单词;
电子邮件的地址是否合规;
2、正则表达式的定义
正则表达式是文本处理方面功能最强大的工具之一。
正则表达式语言用来构造正则表达式,正则表达式用来完成替换和搜索操作。
现在几乎所有的语言都支持正则表达式。
二、正则表达式基础用法
1、匹配单个字符
1.1匹配纯文本
最简单的表达式就是纯文本!!!
apple 就是一个正则表达式。正则表达式可以包含纯文本(甚至只包含纯文本)。
举个例子:
文本
I have an apple.
正则表达式
apple
结果
I have an apple.
这里使用的正则表达式就是纯文本。
注意:正则表达式是区别字母大小写的,所以apple不匹配Apple。
1.2匹配任意字符
. 字符,也就是英文句号,可以匹配任意一个单个字符、字母、数字甚至是 . 字符本身!!!(换行符除外)
举个例子:
文本
hot
h2t
hunt
正则表达式
h.t
结果
hot
h2t
hunt
h.t 表达式匹配出了2个文本,.字符匹配了o和2。
同一个正则表达式中允许使用多个 .字符,也可以间隔出现在表达式的不同位置。
1.3匹配特殊字符
在特殊字符前面加上\ 反斜杠对特殊字符进行转义,即可匹配特殊字符本身!!!
举个例子:
文本
a1.csv
a2.csv
a3b.csv
正则表达式
a.\.
结果
a1.csv
a2.csv
a3b.csv
如果正则表达式采用 a.. 则a3b也会被匹配到。只需给.字符添加\反斜杠字符,就能匹配.字符本身。
假设要匹配 \字符本身,则正则表达式位 \\
2匹配多个字符
2.1匹配多个字符中的一个
在正则表达式中,使用元字符 [和]来定义一个字符集合。在文本中,只需出现[和]集合中任意一个字符即可匹配成功!!!
举个例子:
文本
an1
bn1
cn1
正则表达式
[ab]n.
结果
an1
bn1
cn1
正则表达式中的[]包含了字符a和b,所以文本中 n的前一个字符,只要是[]中的任意一个字符即可匹配成功。
2.2 匹配所有数字和字母
元字符[和]用来定义一个字符集合,其含义是必须匹配该集合里的字符之一!!!
利用[]可以匹配所有数字和所有字母:
[0123456789] 等价于 [0-9]。
[A-Za-z]等价于匹配所有字母。
举个例子:匹配RGB值
正则表达式
#[0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f][0-9A-Fa-f]
结果
color:#333fff
注意:- 字符只有在 [ ]里才有特殊含义,之外只能表示 - 字符本身。
元字符[和]用来定义一个字符集合,其含义是必须匹配该集合里的字符之一。
2.3 取非匹配
使用元字符 ^ 表示取非匹配,[^]表示取这个集合之外的任意字符!!!
举个例子:
文本
an1
bn2
cnX
正则表达式
[abc]n[^0-9]
结果
an1
bn2
cnX
[^0-9]表示除了0-9之外的任意字符。
3.使用元字符
元字符大致分为2种:
- 一种是用来匹配文本的,例如 .字符;
- 另一种是正则表达式语法所要求的,例如[]。
\r 匹配一个回车符
\n 匹配一个换行符
\t 匹配一个制表符
\r \n \t 都是元字符,但是使用 \ 进行转义,只代表字母本身;而 .字符和[]字符,经过\转义之后,代表符号本身。
注意:经过在线正则表达式验证回车符和换行符,一直验证失败,匹配不到。若有大佬知道原因,还请大佬在评论区指教!
3.1 数字元字符
\d 等价于 [0-9],任意一个数字字符
\D 等价于[^0-9],任意一个非数字字符
\w 等价于[a-zA-Z0-9_]
\W 等价于[^a-zA-Z0-9_]
\s 等价于[\f\r\n\t\v],任何一个空白字符
\S 等价于[^\f\r\n\t\v],任何一个非空白字符
3.2 字符集合中使用元字符
在字符集合中, + 字符和 . 字符被视为普通字符,不需要被转义!!!但是转义了也没有坏处。
例如 [\w.] 等价于 [\w\.]
举个例子:
文本
123.&456_.
正则表达式
[\w.]+ 或者 [\w\.]+
结果
123.&456_.
4.重复匹配
4.1 匹配一个或多个字符
匹配同一个字符(或字符集合)的多次重复,只要给这个字符(或字符集合)加上一个+字符作为后缀即可!!!
+字符匹配一个或多个字符(至少一个;不匹配0个的情况);
例如:
a 匹配 a 本身,a+将匹配一个或多个连续出现的a.
[0-9]匹配任意单个数字,[0-9]+将匹配一个或多个连续的数字。
提示:给字符集合加上 + 字符时,都是放在集合的外面。
例子1:
文本
aaabaa
正则表达式
a+
结果
aaabaa
示例说明:a+ 总共匹配了2处,第一处 aaa 第二处 aa,+ 匹配多个字符。
例子2:
文本
123b456
正则表达式
[0-9]+
结果
123b456
说明:[0-9]+ 匹配了2处,第一处 123 ,第二处 456, + 匹配多个字符集合。
提示: + 是元字符,如果要匹配 + 本身,需要使用转义序列 \+.
4.2 匹配0个或多个字符
使用元字符*用来匹配0个或多个字符!!!
*字符的用法与 +字符的用法完全一致,放在一个字符或字符集合的后面,表示该字符或字符集合出现0次或多次。
举个例子:
文本
123@456
abc@456
正则表达式
[\d]*@456
结果
123@456
abc@456
第一行文本 [\d]*匹配了3个数字;
第二行文本匹配了0个数字。这两行文本都成功匹配到了相应的字符串,如果将[\d]*改为[\d]+第二行文本将不满足该正则表达式的规则,匹配不到任何字符。
4.3 匹配0个或1个字符
使用元字符?用来匹配0个或1个字符!!!
有时,需要匹配当下位置出现某一字符一次或者不出现,可以使用该元字符。
举个例子:
文本
http://www.com.cn/123
https://www.com.cn/456
正则表达式
https?://[\w.]+/
结果
http://www.com.cn/123
https://www.com.cn/456
https?: 匹配了http: 和 https: ,如果使用https*,也可以匹配这两个,但是也会匹配到 httpssssss: 这种模式的字符串。
提示:?是一个元字符,如果需要匹配本身,则需要使用转义序列 \?
4.4 为字符匹配设置重复次数
使用{} 元字符来设置字符或字符集合重复匹配的个数或者范围!
{3}设置字符个数必须是3个
{3,}设置字符个数至少为3个
{3,6}设置字符个数至少为3个,最多为6个
举个例子:
文本
1234321@123
正则表达式
[\d]{3}@[\d]{3}
[\d]{3,}@[\d]{3}
[\d]{3,6}@[\d]{3}
结果
1234321@123
1234321@123
1234321@123
[\d]{3}@ 匹配 @前3个字符的串。
[\d]{3,}@ 匹配@前 至少3个最多没有限制,所以匹配了 @前所有的数字 。
[\d]{3,6}@ 匹配了至少3个最多6个的数字,因为文本有7个,所以只匹配了6个数字。
4.5 防止过度匹配
首先看一下什么是过度匹配:
举个例子:
文本:
xx<a>123</a> <h1></h1><a></a>yy
正则表达式
<a>.*</a>
结果
xx<a>123</a> <h1></h1><a></a>yy
本来我们是想匹配 <a>123</a> 和<a></a> 这两个串,但是 .* 表示匹配0个或多个任意字符,导致把第一个 <a> 和 第二个 </a>之间的都符合规则,造成过度匹配。
*字符和 + 字符,被称为贪婪型元字符,如上的场景下就会造成过度匹配!
与之对应的就有懒惰型元字符,用来解决如上场景中遇到的问题:
给 * 或者 +后面加上 ?即可组成懒惰型元字符:*? +?,懒惰型元字符是匹配尽可能少的字符!!!
举个例子:
文本:
xx<a>123</a> <h1></h1><a></a>yy
正则表达式
<a>.*?</a>
结果
xx<a>123</a> <h1></h1><a></a>yy
使用懒惰型元字符,成功匹配出了我们想要的两处字符。
5.子表达式
5.1使用子表达式
子表达式是更大表达式的一部分,把一个表达式分成一系列子表达式的目的是为了将这些子表达式当作一个单独元素使用。子表达式必须用()元字符括起来!!!
举个例子1:
文本
windows 2000 ;100;
正则表达式
( ){2,}
结果
windows 2000 ;100;
分析:上面的例子使用()字符将 作为一个单独的元素进行使用,匹配至少2个 如果使用括号, {2,} 表达式将匹配 ; 两个分号。
注意:()要匹配字符本身,需要使用它 转义序列\( 和\)
举个例子2:
文本
date:1996-09-14
data:2022-05-25
正则表达式
19|20\d{2}
结果
date:1996-09-14
data:2022-05-25
本例中的正则表达式本意是匹配 19或者20开头的年份,实际结果只匹配到了2022,而1996只匹配到了19,是因为\d{2}跟在20后面,只会匹配前面是20的数字,要想也匹配到19开头的数字,可以使用子表达式:(19|20),此时19|20被当成一个元素。
5.2使用嵌套子表达式
子表达式是可以进行嵌套的,嵌套的子表达式只是看起来会比较复杂,但实际分解开来,依然是由最基础的正则表达式的用法。
接下来会举例说明匹配IP地址,不用子表达式嵌套和使用子表达式嵌套实现的不同效果:
举个例子1:
文本
1.12.123.255
300.300.300.300
正则表达式
(\d{1,3}\.){3}\d{1,3}
结果
1.12.123.255
300.300.300.300
上面的正则表达式 (\d{1,3}\.)匹配一个数字后面跟一个. 数字最少是一位数,最多是3位数,子表达式后的{3}表示之前的元素再匹配3次,后面的\d{1,3}表示ip地址的最后一个数字。
根据匹配结果分析,虽然ip地址的基本格式都匹配到了,但是也匹配到了不合法的ip地址,要匹配到合法的ip地址,就需要对ip地址的数字符合相应的规则,可以先了解合法ip地址都有哪些规则:
- 任何一个 1位或2位数字。
- 任何一个以 1开头的三位数字。
- 任何一个以 2开头,第2位数字在 0 - 4 之间的3位数字。
- 任何一个以 25开头,第三位数字在 0-5 之间的3位数字。
满足以上4个条件的,都是合法的ip地址。每一个条件我们都可以用一个子表达式来表示,在用 | 操作符将4个子表达式组成一个更大的子表达式,就会有如下表达式:
((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5])) 其中:
- (\d{1,2}) 匹配任意一位数或2位数 (0 - 99 )
- (1\d{2}) 匹配1开头的任意三位数 (100 - 199)
- (2[0-4]\d) 匹配整数 (200 - 249)
- (25[0-5])匹配整数(250 - 255)
该子表达式用于匹配符合以上4中情况的数字,匹配完整的合法的ip地址,直接上例子:
例子2:
文本
1.12.123.255
300.300.300.300
正则表达式
(((\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]))
结果
1.12.123.255
300.300.300.300
结果分析:(((\d{1,2}) | (1\d{2}) | (2[0-4]\d) | (25[0-5]))\.) 匹配ip地址的数字和. 后面的{3}表示这样的组合来3次,最后一个((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))子表达式匹配ip地址的最后一个数字。
虽然该表达式看着很长,但是拆分为每一个子表达式,都是最基础的用法。
5.3子表达式的另一种用途:回溯引用
上面2个小节总结了子表达的基本用途:将一组字符编组位一个字符集合!!!但子表达式还有另外一个重要用途:回溯引用!!!
首先 不要被它的名字迷惑,回溯引用听起来很难的感觉,实际上是一种简单的子表达式的用法,大致意思是:用正则表达式当前模式的后半部分引用模式前半部分定义的子表达式。比如匹配重复单词。
直接上例子:
文本
this is is a error error!
正则表达式
[ ]+(\w+)[ ]+\1
结果
this is is a error error!
分析: [ ]+匹配一个或多个空格;(\w+) 匹配任意字符,第二个[ ]+匹配一个或多个空格,最关键的就是 \1 ,它就是回溯引用的用法,它表示去匹配第一个子表达式匹配到的字符集合。当(\w+) 匹配到了is ,\1 就匹配is , (\w+)匹配到了 error, \1就去匹配error。
\1 表示引用第一个子表达式匹配到的字符,\2 表示引用第二个子表达匹配到的字符,也可以使用\3 \4 ,如果正则表达式中有这么多子表达时。
为了加深理解,再举一个匹配html文本的例子2:
文本
<h1>111<h1>
<h2>222<h2>
<h1>122<h2>
正则表达式
<h[1-6]>.*?<h[1-6]>
(<h[1-6]>).*?\1
结果1
<h1>111<h1>
<h2>222<h2>
<h1>122<h2>
结果2
<h1>111<h1>
<h2>222<h2>
<h1>122<h2>
第一个表达式 <h[1-6]>.*?<h[1-6]> 可以匹配到相应的格式,但是也匹配到了不合法的文本 <h1>122<h2> 这不是一个合法的html语句。
第二个表达式使用子表达回溯引用,就解决了表达式1的问题, (<h[1-6]) 匹配到了h1 , \1也就只能匹配h1 ,(<h[1-6])匹配h2, \1就匹配h2, 解决了前后标签不一样的问题。
5.4 向前查找和向后查找
向前查找是指 匹配到某字符(或字符集合)之前,不包含该字符!!!使用(?=)来定义
向后查找是指 匹配到某字符(或字符集合)之后,不包含该字符!!!使用(?<=)来定义
直接上例子:
文本
abc$123
正则表达式
\w+(?=\$)
(?<=\$)\w+
结果1
abc$123
结果2
abc$123
\w+(?=\$) 子表达式 (?=\$)表示匹配 $前面的字符集合。\w+匹配到了abc,接下来的字符是$,就停止匹配了。
(?<=\$)\w+ 中 (?<=\$)表示匹配 $字符后面的字符集合,\w+匹配到了123
向前查找和向后查找也可以结合使用
举个例子:
文本
abc$123ddd$abc
正则表达式
(?<=\$)\w+(?=\$)
结果
abc$123ddd$abc
(?<=\$) 子表达式表示匹配 $字符后面的字符集合,\w+匹配数字字母,(?=\$)匹配$之前的字符集合。所以向前查找和向后查找结合可以匹配两个字符之间的字符集合。
5.5 对前后查找取非
上一小节提到的向前向后查找,指的都是正向查找,还有负向的查找,它的意思与正向查找方向相同,将查找不与给定表达式匹配的字符集合。
前后查找各操作符:
(?=) 正向前查找
(?!) 负向前查找
(?<=) 正向后查找
(?<!) 负向后查找
举个例子:
文本
11abc32$
正则表达式
\d+(?=\$)
\d+(?!\$)
结果1
11abc32$
结果2
11abc32$
结果1是正向向前查找,查找$字符前面的数字,32与$的前面且与$相连,符合匹配规则。
结果2是负向向前查找,查找$字符前面的数字,11在$的前面,但是不与$相连,符合负向向前查找匹配规则,3在$的前面,不与$相连,符合负向向前查找的匹配规则。