什么是正则表达式
正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。也就是查找字符串中是否包含满足要求的字符串,譬如我想知道射雕英雄传这本书中“郭靖”出现过多少次,就可以用正则表达式来解决。
正则的发展历史,详情参见1
从正则的发展历史中可以看出,技术的鲜明特性,就是功能导向,而功能之所以有价值是因为有需求,正则之所以被开发和不断发展,是因为有搜素字符串的需求,从需求出发,来理解什么是正则,以及怎样使用正则表达式可能会更好入手。
需求就是解决问题,下面在解决10个问题的过程中说明正则的语法。
问题1: 如何匹配 “abc1de” 中的数字 ?
1 元字符
元字符就可以,元字符是构造正则表达式的最基本元素。常用的元字符如下:
元字符 | 说明 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配任意字母、数字、汉字、下划线 |
\s | 匹配任意的空白符 |
\d | 匹配数字 |
\b | 匹配单词的开始或者结束 |
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
匹配 “abc1de” 中的数字
\d
问题2 :如何匹配" Years may wrinkle the skin, but to give up enthusiasm wrinkles the soul." 中 “w” 开头的单词?
\bw
这就是元字符的使用,有了元字符就可以写一些简单的正则了。
比如: 匹配以 "A"开头的字符串
^A
匹配十一位的手机号码
\d\d\d\d\d\d\d\d\d\d\d
很明显这样的语法并不简洁,匹配一个长度为100的数字序列,难道写100个\d吗 ?
当然不需要,长度限定符可以解决这个问题。
2 长度限定符
为了解决匹配模式的长度问题,正则中有一些定符可以控制模式的长度, 如下:
字符 | 说明 |
---|---|
* | 匹配零次或者任意次 |
+ | 匹配重复一次或者更多次 |
? | 匹配零次或者一次 |
{n} | 匹配重复n次 |
{n,} | 匹配重复n次或多于n次 |
{n,m} | 匹配重复n次到m次 |
{,m} | 匹配重复m次或少于m次 |
那么, 匹配十一位的手机号码记可以改写成
\d{
11}
问题3 :{n,m}匹配n次到m次, 那么具体是匹配多少次呢?
这取决于贪婪还是偷懒
3 贪婪与偷懒
贪婪匹配:(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符,默认是贪婪匹配的,也就是说{n, m} 是在整个表达式得到满足时匹配的尽可能大长度。
偷懒匹配(非贪婪):(在使整个表达式能得到匹配的前提下)匹配尽可能少的字符。懒惰量词是在贪婪量词后面加个"?"
字符 | 说明 |
---|---|
*? | 重复任意次,但尽可能少 |
+? | 重复1或更多次 ,但尽可能少 |
?? | 重复0次或1次,但尽可能少 |
{n,m} ? | 重复n,m次,但尽可能少 |
贪婪与非贪婪的比较
问题4:上诉可以匹配重复的1或2,那么如果我需要12作为整体重复匹配怎么办?
4 分组
正则表达式中用小括号 () 来做分组,也就是括号中的内容作为一个整体。
匹配两个重复的"12"
\12{
2}
问题5:\d可以匹配任意数字,那如果我只想匹配1到5之间的数字怎么办呢 ?
5 区间
正则表达式提供一个元字符中括号 [ ] 来表示区间条件。如下:
符号 | 说明 |
---|---|
[0-9] | 0到9之间的数字 |
[a-z] | a到z之间的字母 |
[A-Z] | A到Z之间的字母 |
比如, 匹配" 大于1小于5的数字?
[1-5]
问题6 :如果我现在需要匹配的是离散的 1,5,7,8呢 ?
集合可以用来匹配集合中的任意一个
6 集合
正则表达式中,符号[xy]也可以表示一个集合,匹配其中的任意字符。
[1578]
或者匹配 a, g, k的其中一个
[agk]
也可以使用条件或
7 条件或
正则用符号 | 来表示或,也叫做分支条件,当满足正则里的分支条件的任何一种条件时,都会当成是匹配成功,就是并的逻辑。
匹配a,g,k的其中一个也可以写成,
a|g|k
匹配 130/166/186/三个号段的号码
(130|166|186)\d{
8}
既然有逻辑条件或,当我们不想匹配某个字符时,非也是并不可少的需求
8 条件非
正则表达式提供一些否定的元字符如下 :
字符 | 说明 |
---|---|
\W | 匹配任意不是字母,数字,下划线,汉字的字符 |
\S | 匹配任意不是空白符的字符 |
\D | 匹配任意不是数字的字符 |
\B | 匹配不是单词开头或结束的位置 |
[^xyz] | 匹配任意不是xyz的字符 |
注意: ^在[ ]中出现表示否定,^单独出现表示字符串开始位置
问题 7:上述出现的(),[ ] ,{ } ,都有特殊用法 , 如果我匹配的就是这些符号本身怎么办呢?
9 转义
针对这种情况,正则提供了转义的方式,也就是要把这些元字符、限定符或者关键字转义成普通的字符,用法很简答,就是在要转义的字符前面加个斜杠\即可。
匹配()
\(\)
所以普通字符加一个转义字符变成特殊符号,如\d , 特殊符号加一个转义字符变成了普通字符,如\ ^表示 ^ 本身。
问题8 : 爬虫时常会有这样的需求,比如在爬取网页中图片的地址时,需要从"< id=“bigImg” src=“https://b.zol-img.com.cn/desk/bizhi/image/10/960x600/98319721647.jpg” " 提取图片的url 。
观察发现,我们需要的是 src= 之后的字符串,零宽断言很好满足您的需求。
10 零宽断言
零宽就是没有宽度,不占字符,只是匹配一个位置。断言就是断定有字符或者没有什么字符 。
正则表达式提供零宽断言有四种,如下
语法 | 名称 | 说明 |
---|---|---|
(?=x) | 正向前行断言 | 匹配任意x之前的位置 |
(?<=x) | 正向后行断言 | 匹配任意x之后的位置 |
(?!x) | 负向前行断言 | 匹配任意不是x之前的位置 |
(?<!x) | 负向后行断言 | 匹配意不是x之前的位置 |
零宽断言的字符比较好记,特殊字符(?)表示零宽断言, = 表示正向断言,!表示否定,<表示在x后面的位置, 没有<则是前面的位置。
上图为很实用的正则化练习网站 https://regex101.com/
匹配图片地址,
(?<=src=)\".*\"
问题9:现在需要将字符串" location: China, date : 2020-06-01,weather : cloudy "中的日期匹配出,并且分别调用其中的年月日 。
这就不仅仅要匹配所需字符串而且要将匹配的结果分组。
11 捕获组
捕获组就是把正则表达式中子表达式匹配的内容,保存到以数字编号或者命名的组里,方便后面引用。
捕获组的只需要括号就可以(x), 也可以将捕获的组命名,语法为(?<name>x) ,不命名就自动编号。
比如捕获日期(数字编号),
捕获组并且命名,
python中groups方法可以调用捕获组。 python正则函数详细用法,参见2
捕获组不仅可以在外部调用,也可以在正则表达式中调用,比如解决下面的问题。
问题10 : 查找一串字母"5443114994002568856643"里连续相同的数字。
12 反向引用
捕获组捕获到的内容,不仅可以在正则表达式外部通过程序进行引用,也可以在正则表达式内部进行引用,这种引用方式就是反向引用。
对于普通捕获组和命名捕获组的引用,语法如下:
普通捕获组反向引用:\k,通常简写为\number
命名捕获组反向引用:\k或者\k’name’
解释一下就是,捕获组(\d)捕获的是一个数字,第一组就是这个数字, 引用第一组,就和第一组一样,所以匹配的是两个相同的数字。
以上就是正则的基本语法,实战中往往组合起来使用,熟练掌握还是得多练习多使用。
本文是正则表达式的语法梳理,不涉及正则表达式的原理,正则原理是优化正则的前提,也是我们必须了解的部分,原理参照1