【Python】 正则

基础正则

字符串

表达式 描述
[abc] 字符集。匹配集合中所含的任一字符。
[^abc] 否定字符集。匹配任何不在集合中的字符。
[a-z] 字符范围。匹配指定范围内的任意字符。
. 匹配除换行符以外的任何单个字符。
\ 转义字符。
\w 匹配任何字母数字,包括下划线(等价于[A-Za-z0-9_])。
\W 匹配任何非字母数字(等价于[^A-Za-z0-9_])。
\d 数字。匹配任何数字。
\D 非数字。匹配任何非数字字符。
\s 空白。匹配任何空白字符,包括空格、制表符等。
\S 非空白。匹配任何非空白字符。

边界

表达式 描述
^ 匹配字符串或行开头。
$ 匹配字符串或行结尾。
\b 匹配单词边界。比如World\b可以匹配HelloWorld末尾的World,不能匹配HelloWorld1中的World
\B 匹配非单词边界。比如World\B可以匹配HelloWorld1中的World,不能匹配HelloWorld中的World

数量

表达式 描述
? 匹配前面的表达式0个或1个。即表示可选项。
+ 匹配前面的表达式至少1个。
* 匹配前面的表达式0个或多个。
| 或运算符。并集,可以匹配符号前后的表达式。
匹配前面的表达式m个。
匹配前面的表达式最少m个。
匹配前面的表达式最少m个,最多n个。

分组引用

表达式 描述
(expression) 分组。匹配括号里的整个表达式。
(?:expression) 非捕获分组。匹配括号里的整个字符串但不获取匹配结果,拿不到分组引用。
\num 对前面所匹配分组的引用。比如(\d)\1可以匹配两个相同的数字,(h)(a)\1\2则可以匹配haha。

预查断言

表达式 描述
(?=) 正向预查。比如h(?=e)能匹配hello中的h, 但不能匹配home中的h
(?!) 正向否定预查。比如h(?!e)不能匹配hello中的h, 但能匹配home中的h
(?<=) 反向预查。比如(?<=l)o能匹配hello中的o, 但不能匹配home中的o
(?<!) 反向否定预查。比如(?<!l)o不能匹配hello中的o, 但能匹配home中的o

非贪婪模式 - {x,y}?

非贪婪模式是指在使用正则匹配时,尽可能少的匹配(默认是贪婪模式,即:尽可能多的匹配)。例

re.search(r'[\d]{2,5}?','091234568')
<_sre.SRE_Match object; span=(0, 2), match='09'>

在这里{2,5}?匹配只是匹配2-5个[\d]时只要满足2(最少的)个就好,在看看贪婪模式:

re.search(r'[\d]{2,5}','091234568')
<_sre.SRE_Match object; span=(0, 5), match='09123'>

这时候,匹配2-5个[\d]时,默认匹配最多的5个。

注意:贪婪和非贪婪模式的区别就是重复操作符后有没有?字符

分组

正则表达式提供了一个机制将表达式分组,匹配的结果也将按照表达式单独分组。例:

>>> m = re.search(r'(\d{3})-(\d{5})','029-25642')
>>> m.group()
'029-25642'
>>> m.groups()
('029', '25642')
>>> m.group(2)
'25642'
  • m.groups() 看到分组匹配结果,
  • m.group(index) 查看具体编号的分组结果(编号从1开始,0是完整的匹配)

那分组有什么用呢,好像也没什么特殊的含义,不急,下面会用到。

引用分组(回溯) - \N

有这么一种情况,比如假设我要找出一个html文本中的所有<a></a>标签,怎么办?试试这样:

>>> re.search(r'<(\w+)>.+</(\w+)>','<a>this is a demo</e>')
<_sre.SRE_Match object; span=(0, 21), match='<a>this is a demo</e>'>

奇怪的事情来了,为什么<a></e>被匹配成功了,显然结果并不是想要的,那怎么才能只匹配<a></a>而过滤掉其他的呢(比如<a></e>)?答案就是引用分组,例:

>>> re.search(r'<(\w+)>.+</\1>','<a>this is a demo</e><p>demo two</p>')
<_sre.SRE_Match object; span=(21, 36), match='<p>demo two</p>'>  

这里\1是关键,意思就是当前位置匹配的结果需要和第一个分组匹配的结果一致,或者说第一个分组的匹配结果期望在这里再次出现。以此类推。该方法最多只能匹配前99个分组。

分组命名 - (?P<name>.*)

分组命名最开始由python引入,比如Django路由中会用到。分组命名的好处是方便,直接使用名字比编号要简单而且不会变化,例:

>>> m = re.search(r'(?P<first_name>\d{3})-(?P<second_name>\d{4})','029-8967')
>>> m.group('first_name')
'029'
>>> m.groupdict()
{'first_name': '029', 'second_name': '8967'}

当然,命名分组仍然是编号分组,依然可以使用编号进行查找分组。

先行断言 - X(?!Y)、X(?=Y)

假设有这么一种情况,要查找所有163信箱的文本,也就是@163.com结尾的所有email账号信息,也就是说不要@163.com这部分,但是其还要参与匹配。这就用到了先行断言,也即基于之后的内容是否存在接收或拒绝一个匹配,而不需要接下来的内容作为匹配的一部分。例:

>>> re.search(r'h(?!e)','hello home!')
<_sre.SRE_Match object; span=(6, 7), match='h'>

h(?!e)表示匹配h,而且h后面不能是e,此处匹配成功的是home,但是只返回h

>>> re.search(r'h(?=e)','hello home!')
<_sre.SRE_Match object; span=(0, 1), match='h'>

h(?=e)表示匹配he,此处匹配成功的是hello,但是只返回h

标记

标记 作用 解释
re.IGNORECASE(re.I) 不区分大小写 使得正则表达式不区分大小写
re.DOTALL(re.S) 点匹配换行符 使得 . 符号可以匹配换行符
re.MULTILINE(re.M) 多行模式 使得^$字符可以匹配任意行的开始与结束
re.VERBOSE(re.X) 详细模式 使得正则表达式可以换行书写,且可以加入注释
re.DEBUG 调试模式 将调试信息输出到sys.stderr
调试模式:re.DEBUG-将调试信息输出到sys.stderr
posted @ 2022-05-31 17:53  倒骑驴子  阅读(39)  评论(0编辑  收藏  举报