正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表达式通常被用来检索、替换那些符合某个模式(视则)的文本。
正则表达式可以:
(1)测试字符串的某个模式,即数据有效性验证(查找)
(2)实现按照某种规则替换文本
(3)根据模式匹配从字符串中提取一个子字符串(爬虫)
正则表达式的构成: 原子(普通字符,如英文字符)、元字符(有特殊功能的字符)、以及模式修正字符组成。注意:一个正则表达式中至少包含一个原子。
正则表达式测试工具: RegexBuddy
字符 | 功能 |
---|---|
. | 匹配任意1个字符(除了\n 换行) |
[] | 匹配[ ]中列举的字符 |
\d | 匹配数字,0-9 |
\D | 匹配非数字,即不是数字 |
\s | 匹配空白,即空格、\t tab键、\n 换行 |
\S | 匹配非空白 |
\w | 匹配单词字符(字母、数字、下划线),即a-z、A-Z、0-9,、_ |
\W | 匹配非单词字符(非字母、非数字、非下划线) |
字符 | 功能 |
---|---|
* | 匹配前一个字符出现0次或者无限次,即可有可无 |
+ | 匹配前一个字符出现1次或者无限次,即至少有1次 |
? | 匹配前一个字符出现1次或者0次,即要么有1次,要么没有 |
{m} | 匹配前一个字符出现m次 |
{m,n} | 匹配前一个字符出现从m到n次,m |
字符 | 功能 |
---|---|
^ | 匹配字符串开头,注意^[4-7]和[^4-7]的区别 |
$ | 匹配字符串结尾 |
注意: ^有两个作用
(1)匹配已指定字符开头
(2)用在[]内部,用于取反,如:[^he]
匹配不含有h和e的字符
练习:
1、匹配出,一个字符串第一字母为大写字符,后面都是小写字母并且这些小写字母可有可无:^[A-Z][a-z]*
2、匹配出,变量名是否有效(由字母、数字、下划线组成,不能以数字开头):^[a-zA-Z_]+\w*
3、匹配出,0到99之间的数字:^\d?\d$
4、匹配出,不是以4、7结尾的手机号码(11位):^\d{10}[0-35689]$
5、匹配出,8到20位的密码,可以是大小写英文字母、数字、下划线:^\w{8,20}$
6、匹配出,163的邮箱地址,且@符号之前有4到20位,例如[email protected]:^\w{4,20}@163\.com$
re模块是python中提供的用于正则操作的模块。
re模块的使用步骤:
导入模块:import re
使用match()方法进行检测:result = re.match('正则表达式', '要验证/检测的字符串’)
判断是否检测/匹配成功:print('匹配成功') if result else print('匹配失败')
取出匹配的具体内容:result.group()
从头匹配一个符合规则的字符串,从起始位置开始匹配,匹配成功返回一个对象,未匹配成功返回None
pattern:正则模型
string:要匹配的字符串
flags:匹配模式
注:这个方法并不是完全匹配。当pattern结束时若string还有剩余字符,仍然视为成功。想要完全匹配,可以在表达式末尾加上边界匹配符$
match()方法一旦匹配成功,就是一个match object对象,而match object对象有以下方法:
"""匹配出文章的阅读次数"""
import re
result = re.match('\d+', '9999 - 阅读次数') # 匹配成功!结果: 9999
# match从头开始匹配
# result = re.match('\d+', '阅读次数为 9999') # 匹配失败!结果: None
if result:
print('匹配成功!结果:', result.group())
else:
print('匹配失败!结果:', result)
re.search函数会在字符串内查找模式匹配,只要找到第一个匹配然后返回,如果字符串没有匹配,则返回None。
"""匹配出文章的阅读次数"""
import re
result = re.search('\d+', '阅读次数为 9999') # 匹配成功!结果: 9999
if result:
print('匹配成功!结果:', result.group())
else:
print('匹配失败!结果:', result)
match()和search()的区别:
match()函数只检测RE是不是在string的开始位置匹配,search()会扫描整个string查找匹配;
也就是说match()只有在0位置匹配成功的话才有返回,如果不是开始位置匹配成功的话,match()就返回none。
如:print(re.match('super','superstar').span())
会返回(0,5);print(re.match('super', 'insuperable'))
,则返回None。
如:print(re.search'super'','superstar).span())
,返回(0,5),print(re.search('super','insuperable').span())
,返回(2,7)
re.findal()遍历匹配,可以获取字符串中所有匹配的字符串,返回一个列表。
"""匹配出阅读次数、转发次数和评论次数"""
import re
# result = re.search('\d+', '阅读次数:9999,转发次数:6666,评论次数:88')
result = re.findall('\d+', '阅读次数:9999,转发次数:6666,评论次数:88')
if result:
# print('匹配成功!结果:', result.group()) # 匹配成功!结果: 9999
print('匹配成功!结果:', result) # 匹配成功!结果: ['9999', '6666', '88']
else:
print('匹配失败!结果:', result)
使用re替换string中每一个匹配的子串后返回替换后的字符串。
实例:将匹配到的阅读次数加1
import re
"""方法一"""
result = re.sub('\d+', '10000', '阅读次数:9999,转发次数:6666,评论次数:88', 1)
if result:
print('匹配成功!结果:', result) # 匹配成功!结果: 阅读次数:10000,转发次数:6666,评论次数:88
else:
print('匹配失败!结果:', result)
"""方法二"""
def add(temp):
strNum = temp.group()
num = int(strNum) + 1
return str(num)
result = re.sub('\d+', add, '阅读次数:9999,转发次数:6666,评论次数:88', 1)
if result:
print('匹配成功!结果:', result) # 匹配成功!结果: 阅读次数:10000,转发次数:6666,评论次数:88
else:
print('匹配失败!结果:', result)
按照能够匹配的子串将string分割后返回列表。
可以使用re.split来分割字符串,如:re.split(r'\s+', text)
,将字符串按空格分割成一个单词列表。
"""切割字符串info:xiaoZhang 33 shandong"""
import re
# re.split("正则表达式", “待拆分的字符串") 按照正则拆分字符串,返回值是一个列表
result = re.split(r':| ', 'info:xiaoZhang 33 shandong')
if result:
print('匹配成功!结果:', result) # 匹配成功!结果: ['info', 'xiaoZhang', '33', 'shandong']
else:
print('匹配失败!结果:', result)
字符 | 功能 |
---|---|
| | 匹配左右任意一个表达式 |
(ab) | 将括号中字符作为一个分组 |
\num | 引用分组num匹配到的字符串 |
(?P |
分组起别名为name |
(?P=name) | 引用别名为name分组匹配到的字符串 |
|的作用:或者关系,多个正则表达式满足任意一个都可以。
匹配出0-100之间的数字:^[0-9]?[0-9]$|^100$
(满足^[0-9]?[0-9]$或者满足^100$任意一个,返回值都是一个match object对象)
()的作用:
(1)分组,整体匹配。例如:匹配出163、126、qq邮箱,且@符号之前有4到20位:^\w{4,20}@(163|126|qq).com$
(2)提取子字符串。例如:提取区号和电话号码:
import re
result = re.match('(\d{3,4})-(\d{7,8})', '010-12345678')
if result:
print('匹配成功!')
print('提取区号:', result.group(1))
print('提取电话号码:', result.group(2))
else:
print('匹配失败!')
运行结果:
匹配成功!
提取区号: 010
提取电话号码: 12345678
\的作用:引用分组。\1表示引用第一组,\是转义字符,转义后代表一个
匹配出2个标签页中的值,如:hhaaaaa
import re
# 注意:\1 有特殊用法; \\-->\
result = re.match('<([a-zA-Z0-9]+)><([a-zA-Z0-9]+)>(.*)\\2>\\1>', 'hhaaaaa
')
if result:
print('匹配成功!')
print('匹配结果:', result.group(3))
else:
print('匹配失败!')
运行结果如下:
匹配成功!
匹配结果: hhaaaaa
起名:?P
引用别名:?P=name
import re
# ?P 给分组起别名,别名为name1
# ?P=name1 引用别名为name1的分组
result = re.match('<(?P[a-zA-Z0-9]+)><(?P[a-zA-Z0-9]+)>(.*)(?P=name2)>(?P=name1)>' , 'hhaaaaa
')
if result:
print('匹配成功!')
print('匹配结果:', result.group(3))
else:
print('匹配失败!')
运行结果如下:
匹配成功!
匹配结果: hhaaaaa
贪婪: 默认,表示在满足正则的情况下尽可能多的取内容
非贪婪: 表示在满足正则的情况下,尽可能少的取内容
贪婪转变为非贪婪: 在* ? + {m, n}后面加上?就可以了
import re
# 非贪婪:满足正则的情况下,尽可能少的取内容
result = re.search('data-imgurl=\"(.*?)\"', '') # 匹配成功!结果: https://img1.baidu.com/it/u=1546227440,2897989905&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500
# 贪婪:满足正则的情况下,尽可能多的取内容。
result = re.search('data-imgurl=\"(.*)\"', '') # 匹配成功!结果: https://img1.baidu.com/it/u=1546227440,2897989905&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500" src="data:image/jpeg;base64,/9j/4AAQSkZJRgAB" style="width: 258px; height: 160px;
if result:
print('匹配成功!结果:', result.group(1))
else:
print('匹配失败!结果:', result)
Python中在正则字符串前面加’r’表示,让正则中的’\'不再具有转义功能(默认为转义),就是表示原生字含义一个斜杠\,仅仅对\起作用。
原来的写法:result= re.match("<([a-zA-Z0-9]*)>.*\\1>", "helloworld")
使用r的写法:result= re.match(r"<([a-zA-Z0-9]*)>.*\1>", "helloworld")
说明:
与大多数编程语言相同,正则表达式里使用“\”作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符“\”,那么使用编程语言表示的正则表达式里将需要4个反斜杠“\”:前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。
Python里的原生字符串很好地解法了这个问题,有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。