正则表达式一般用来在文本中查找和替换字符串,再简单的文本中我们可以直接使用字符串查找,但是在大量数据和复杂结构中查找指定字符串不方便并且效率低,因此学习正则表达式显得非常有必要。
正则表达式字符串是有普通字符和元字符组成。
· 普通字符,是指按照字面意义表示的字符,比如abcd。
· 元字符,是预先定义好的一些特定字符,比如下面正则表达式中的 \w 以及 . ,都属于元字符。
rep = r'\w+@mail2\.sysu\.edu\.cn' # 匹配域名为mail2.sysu.edu.cn的邮箱
元字符是用来描述其他字符的特殊字符,它是由基本元字符+普通元字符构成。
字符 | 说明 |
---|---|
\ | 转义字符 |
. | 表示任意一个字符 |
+ | 表示重复一次或多次 |
* | 表示出现零次或多次 |
| | 表示或关系,比如A |
{ } | 定义量词 |
[ ] | 定义字符类 |
( ) | 定义分组 |
^ | 表示取反,或匹配一行的开始 |
$ | 匹配一行的结束 |
- | 表示区间 |
\ 转义符: 比如上面的 \. ,实际上是为了匹配单个 . 符号,但是 . 本身又作为元字符表示任意一个字符,因此遇到这种情况需要转义。
^ 和 $: 这个时候不仅要求匹配字符串,而且要求字符串出现在文本的开始或者结尾,例如:
text = 'My email address is [email protected]'
rep1 = r'\w+@mail2\.sysu\.edu\.cn' # r表示原始字符,避免在正则表达式中输入转义
rep2 = r'^\w+@mail2\.sysu\.edu\.cn$' # 添加了^和$,对开头结尾有要求
# \w表示任意一个字母或数字 +表示出现一次或多次
像上面这段文本,需要将邮箱提取出来的话,只能使用rep1,因为rep2要求匹配的字符串是一行的开头和结尾,只有在邮箱是单独一行才能提取出来。
字符类:字符类用中括号括起来,只要其中一个字符满足条件即可,例如:
text1 = 'I like MATLAB and Python.'
text2 = 'I like MATLAB and python.'
rep = r'[Pp]ython' # 等效于 python1|Python
# 上述两个文本中的python都可以用下面的正则表达式匹配出来。
^,取反符:有时候,就不需要某些字符出现,可以用取反符,例如我们需要找到非数字的字符:rep = r'^[0123456789]'
,这个正则表达式就可以匹配一个非数字的字符。
-,表示区间:比如上面的[0123456789],可以直接写成[0-9],这个区间可以是分段的,比如[0-35-9],这个就表示除4以外的数字。
字符 | 说明 |
---|---|
\. | 匹配 . |
\ | 匹配\ |
\n | 匹配换行 |
\r | 匹配回车 |
\f | 匹配翻页符 |
\t | 匹配水平制表符 |
\v | 匹配垂直制表符 |
\s | 匹配一个空格符,等价于[\t\n\r\f\v] |
\S | 匹配一个非空格符,等价于[^\s] |
\d | 匹配一个数字,等价于[0-9] |
\D | 匹配一个非数字,等价于[^\d] |
\w | 匹配任何语言单词字符、数字、下划线等,编码为ASCII的时候只能是英语 |
\W | 等价于[^\w] |
字符 | 说明 |
---|---|
? | 出现零次或一次 |
* | 出现零次或多次\ |
+ | 出现一次或多次 |
{n} | 出现n次 |
{n , } | 至少出现n次 |
{n , m} | 出现n次到m次之间 |
默认情况都是匹配的字符串越多越好,有时候要尽可能少的匹配,就需要在后面加?,表示懒惰匹配,我们具体写一个例子:
import re
text = '123456789'
rep1 = r'\d{5,8}' # 匹配12345678
m = re.search(rep1,text)
print(m)
rep2 = r'\d{5,8}?' # 匹配12345
m = re.search(rep2,text)
print(m)
字符分组就是将一个子字符串可看成一个整体,进行匹配,举个例子:
import re
text = '12312311aa'
rep = r'(123)+' # 123出现一次或多次
rep1 = r'123+' # 3出现一次或多次
m = re.search(rep1,text)
print(m)
m = re.search(rep2,text)
print(m)
这样有什么好处呢?我们举一个具体的例子,提取一个电话号码的区号和号码:
import re
tel = '0715-53566663'
rep = r'(\d{3,5})-(\d{6,9})'
m = re.search(rep,tel)
print(m)
print(m.group()) # 返回匹配的所有字符串
print(m.groups()) # 返回匹配所有字符串,用元组装起来
这样,可以将区号和电话号码有效分开,除此之外还可以给分组命名:
import re
tel = '0715-53566663'
rep = r'(?P\d{3,5})-(?P\d{6,9})'
m = re.search(rep,tel)
print(m)
print(m.group()) # 返回匹配的所有字符串
print(m.groups()) # 返回匹配所有字符串,用元组装起来
print(m.group('area_code')) # 通过分组名称来引用
print(m.group('Phone_Number'))
我们在爬取网页的时候,常常会出现标签,标签的开始和结束应该是一致的,下面举例:
import re
text1 = 'Python Code '
text2 = 'Python Code' # 这种情况是我们不想要的
rep1 = r'<(\w+)>.*(\w+)>'
m = re.search(rep1,text1)
print(m)
m = re.search(rep1,text2) # 同样匹配了
print(m)
import re
text1 = 'Python Code '
text2 = 'Python Code'
rep1 = r'<(\w+)>.*\1>' # 反向引用在这里,用 \1 代替了(\w+)这个分组,表示要和前面匹配的分组相同,1代表的是分组序号
m = re.search(rep1,text1)
print(m)
m = re.search(rep1,text2)
print(m)
前面讲都是捕获分组,这些匹配的结果都被暂时存放在内存中,但是有时候我们只需要一些分组进行辅助匹配,但是不想保存,就可以使用非捕获分组,举一个例子,匹配.jpg文件名:
import re
text = 'im1.jpg,im2.jpg,im3.png'
rep = r'\w+(.jpg)' (.jpg)为捕获分组
m = re.findall(rep,text) # 找出所有匹配的结果
print(m)
rep = r'\w+(?:.jpg)' (?:.jpg)为非捕获分组
m = re.findall(rep,text)
print(m)
使用捕获分组的时候,只会讲捕获分组里面的打印出来,这并不是我们所希望的。
re 模块是python提供的关于正则表达式的模块,可以直接使用,下面介绍里面的主要函数。
search()和match()函数在使用上非常相似,区别如下:
import re
text = 'My email address is sishiwu@mail2.sysu.edu.cn.'
rep = r'\w+@mail2\.sysu\.edu\.cn' # 匹配域名为mail2.sysu.edu.cn的邮箱
m = re.search(rep,text)
print(m)
m = re.match(rep,text)
print(m)
match与我们之前提到的 ^ 和 $ 的作用相似,一般用于验证结果。
匹配结果返回的都是一个match对象,它常用的方法包括:group(),groups(),start(),end(),span().
import re
text = 'My email address is sishiwu@mail2.sysu.edu.cn.'
rep = r'(\w+@mail2\.sysu\.edu\.cn)' # 匹配域名为mail2.sysu.edu.cn的邮箱
m = re.search(rep,text)
print(m)
print(m.groups())
print(m.group())
print(m.start())
print(m.end())
print(m.span())
分割 split()
re.split(pattern, string, count(可选) , flags (可选))
pattern是正则表达式 , string是字符串, count是最大分割次数, flags是编译标志
import re
p = r'\d+' # 表示至少一个数字
text = 'AB12CD23EF'
m = re.split(p,text)
print(m)
替换 sub()
re.sub(pattern, sym, text, count, flags)
pattern是正则表达式, sym是替换的字符串, text是查找的文本, count替换的次数, flags编译标志
import re
p = r'\d+' # 表示至少一个数字
text = 'AB12CD23EF'
m = re.sub(p, '#', text)
print(m)
为了能够重复使用正则表达式,并且提高程序运行速度,可以使用编译后正则表达式,返回一个regex对象。
import re
p = r'\d+' # 表示至少一个数字
rep = re.compile(p) # 注意
text = 'AB12CD23EF'
m = rep.sub('#', text) # 注意,虽然函数名一样,但是一个是rep对象的方法,一个是re模块中的函数
# 输入参数也变化了
print(m)