Python中的正则表达式
1. 概述
正则表达式,又称正规表示式、正规表示法、正规表达式、规则表达式、常规表示法(英语:Regular Expression,在代码中常简写为regex、regexp或RE),是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些匹配某个模式的文本。
Regular Expression的“Regular”一般被译为“正则”、“正规”、“常规”。此处的“Regular”即是“规则”、“规律”的意思,Regular Expression即“描述某种规则的表达式”之意。
在Django中,url就可以使用正则表达式来进行匹配
2. re模块
在Python中需要通过正则表达式对字符串进行匹配的时候,可以使用一个模块,名字为re
re模块的使用过程
# 使用match方法进行匹配操作
result = re.match(正则表达式,要匹配的字符串)
# 如果上一步匹配到数据的话,可以使用group方法来提取数据
result.group()
re.match是用来进行正则匹配检查的方法,若字符串匹配正则表达式,则match方法返回匹配对象(Match Object),否则返回None(注意不是空字符串”“,因为空字符串也可以认为是一种匹配的结果)。
匹配对象Macth Object具有group方法,用来返回字符串的匹配部分。
re模块示例
例如我们要匹配以”lanou”开头的字符串
import re
result = re.match("lanou", "lanou.com")
result.group()
运行结果为:
lanou
说明
re.match()方法可以匹配出以xxx开头的字符串,可以认为是从目标字符串中提取出符合规则的字符串,默认的匹配规则是从目标字符串的开头进行匹配的,例如
import re
result = re.match("lanou", "hello lanou.com")
就不满足匹配要求,result为None,而group()方法可以将匹配成功的部分提取出来,值得注意的是,目前这种写法虽然使用了正则,但是却没有任何的实际意义,因为匹配规则不具备任何的可变化性,正则表达式的部分,核心就在于其中匹配规则的编写
3. 表示字符
正则表达式中的匹配规则本质上就是一个字符串,但是会认为的规定一些特殊的字符来代表某些特别的含义,以下是表示单字符的规则
字符 | 功能 |
---|---|
. | 匹配任意1个字符(除了\n) |
[] | 匹配[ ]中列举的字符 |
\d | 匹配数字,即0-9 |
\D | 匹配非数字,即不是数字 |
\s | 匹配空白,即 空格,tab键 |
\S | 匹配非空白 |
\w | 匹配单词字符,即a-z、A-Z、0-9、_ |
\W | 匹配非单词字符 |
这些字符在匹配规则中都只代表一个字符,也就是说不管怎么写,都只能对应着目标文本中的一个字符
.
. 可以匹配任意字符,但是不能匹配换行符
可以看到一个.(点)可以匹配一个任意字符,匹配的时候从目标字符串开始找,只要有能符合要求的,就认为是符合要求的
[]
当某一位的匹配规则没办法直接表明的时候,就需要使用[]来进行限定,一对中括号中的所有表达式在一起,组成了限定条件,共同限定一个字符,例如我们要匹配以H或h开头的字符:
可以看到一对中括号中可以包含多个字符,也可以包含上表中的任意字符,默认是用或的关系来进行匹配的,但是无论怎么写,默认情况下它们只匹配一个字符
^
如果想取反,则需要在中括号内加上^符号,例如要匹配不以H或h开头的字符串
注意: ^仅在中括号内才代表取反的意思,如果脱离中括号,则不代表取反
4. 原始字符串
如果我们要匹配一个路径,例如: a\b\c
,那我们的目标字符串在实际输入的时候就是
s = "a\\b\\c"
需要将转义符给转义了,而要想匹配这个字符那么条件字符串就需要能够连续匹配两个转义字符,例如
re.match("a\\\\b",s)
要匹配2个转义字符,就需要4个转义字符,写起来太不方便了,所有python中提供了r,来标记原始字符串
r的作用是会自动的为转义字符添加上转义字符:
5. 表示数量
现在的正则表达式只能够一个字符一个字符的匹配,在正则的规则中还有一些表示数量的字符:
字符 | 功能 |
---|---|
* | 匹配前一个字符,出现0次或者无限次,即可有可无 |
+ | 匹配前一个字符,出现1次或者无限次,即至少有1次 |
? | 匹配前一个字符,出现1次或者0次,即最多1次 |
{m} | 匹配前一个字符,出现m次 |
{m,} | 匹配前一个字符,至少出现m次 |
{m,n} | 匹配前一个字符,需要出现m~n次 |
示例
匹配11位手机号:
- 第一位固定是1
- 后面10位均是数字
6. 表示边界
在正则匹配中,还需要边界的表示,例如在匹配手机号的时候,目标字符串只要前11位是满足条件的,就能匹配成功,例如:
这就需要边界条件来限定截止
字符 | 功能 |
---|---|
^ | 匹配字符串开头 |
$ | 匹配字符串结尾 |
\b | 匹配一个单词的边界 |
\B | 匹配非单词的边界 |
示例:
匹配11位手机号
7. 匹配分组
字符 | 功能 |
---|---|
| | 匹配左右任意一个表达式 |
(ab) | 将括号中字符作为一个分组 |
\num | 引用分组num匹配到的字符串 |
(?P) | 为分组起别名 |
(?P=name) | 引用别名为name分组匹配到的字符串 |
分组
除了简单地判断是否匹配之外,正则表达式还有提取子串的强大功能。用()表示的就是要提取的分组(Group)。比如:
^(\d{3})-(\d{3,8})$
分别定义了两个组,可以直接从匹配的字符串中提取出区号和本地号码
如果正则表达式中定义了组,就可以在Match对象上用group()方法提取出子串来。
注意到group(0)永远是原始字符串,group(1)、group(2)……表示第1、2、……个子串。
示例
- 匹配0-100之间的数字
- 第一位不能为0
- 可能是1位数,2位数,3位数
- 如果是1位数,则第一位可以为0
- 匹配出一对标签中的内容
- 标签会成对出现,例如
- 标签名需要和结束标签名相同
8. re模块的其他用法
search
查找,可以查找出符合条件的字符串,与match模块不同在于,search模块并不固定从目标字符串的开头查找,而是会从目标串中一直查找,直到找到符合条件的,例如匹配出文章阅读次数:
import re
ret = re.search(r"\d+", "阅读次数为999")
ret.group()
findall
会匹配所有符合条件的字符串,返回列表
import re
ret = re.findall(r"\d+", "python = 9999, c = 7890, c++ = 12345")
print(ret)
运行结果为:
sub
将匹配到的数据进行替换,例如将匹配到的阅读次数加1
import re
ret = re.sub(r"\d+", '998', "python = 997")
print(ret)
方法2:
import re
def add(temp):
strNum = temp.group()
num = int(strNum) + 1
return str(num)
ret = re.sub(r"\d+", add, "python = 997")
print(ret)
ret = re.sub(r"\d+", add, "python = 99")
print(ret)
方法3:
lambda表达式
import re
ret = re.sub(r"\d+", lambda x : str(int(x.grooup()) + 1), "python = 997")
split
分割字符串,根据匹配进行切割字符串,返回一个列表,匹配的字符串就被干掉了,和str的分割字符串方法一样
9. 贪婪和非贪婪
Python里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;
非贪婪则相反,总是尝试匹配尽可能少的字符。
在”*”,”?”,”+”,”{m,n}”后面加上?,使贪婪变成非贪婪。
例如从
<img data-original="https://rpic.douyucdn.cn/appCovers/2016/11/13/1213973_201611131917_small.jpg" src="https://rpic.douyucdn.cn/appCovers/2016/11/13/1213973_201611131917_small.jpg" style="display: inline;">
中提取jpg图片
可以看到,因为.*的存在,python想要匹配尽可能多的字符,反而匹配的范围超标了
通过在不确定个数的时候,尽可能少的匹配字符
10. 编译
当我们在Python中使用正则表达式时,re模块内部会干两件事情:
1. 编译正则表达式,如果正则表达式的字符串本身不合法,会报错
2. 用编译后的正则表达式去匹配字符串
如果一个正则表达式要重复使用几千次,出于效率的考虑,我们可以预编译该正则表达式,接下来重复使用时就不需要编译这个步骤了,直接匹配
编译后生成Regular Expression对象,由于该对象自己包含了正则表达式,所以调用对应的方法时不用给出正则字符
11. 练习
写一个方法,给定一个字符串,需要提取第一个单词
- 字符串中可能有.和,
- 一个字符串可以以字母或空格或.开头
- 一个词可能包含’,他是一个词的一部分
- 整个文本可以用一个单词表示
例如:
first_word("Hello world") == "Hello"
first_word("greetings, friends") == "greetings"
示例:
import re
def first_word(text: str) -> str:
return re.search("([\w']+)", text).group(1)