正则表达式
一、概述
- 概念
比如说:在实际开发中,可以需要验证注册用户的名称是否满足某种(使用字母和下划线),程序员需要对每个用户输入的内容进行规则的对比;
再比如:需要爬取页面中内容,邮箱(xxx@域名)、手机号、图片的链接;
正则表达式就是满足某种规则的一段代码。英文名称:Regular Expression
,简称RE
- 特点
- 语法比较诡异,可读性很差
- 通用性很强,绝大多数的编程语法都正则表达式。
二、re模块
# 导入re模块
import re
# 使用match函数进行匹配,match默认从开头开始匹配
r = re.match(r'测试', '测试开发57')
# 打印匹配结果
print(r.group()) # 测试
三、匹配单个字符
代码 | 功能 |
---|---|
. | 匹配任意一个字符,不能匹配\n |
[] | 匹配[] 中列举的字符 |
\d | 匹配数字,即0-9 |
\D | 匹配非数字,即不是数字 |
\s | 匹配空白,即\n 、\t |
\S | 匹配非空白字符 |
\w | 匹配非特殊字符,即a-z、A-Z、0-9、下划线、汉字 |
\W | 匹配特殊字符,即非数字、非字母、非汉字 |
\d
和\D
# \d
r = re.match(r'测试开发\d', '测试开发57')
print(r.group())
r = re.match(r'测试开发\d', '测试开发A')
print(r.group()) # 匹配失败
# \D
r = re.match(r'测试开发\D', '测试开发A')
print(r.group()) # 匹配成功
r = re.match(r'测试开发\D', '测试开发!')
print(r.group()) # 匹配成功
\w
和\W
# \w
r = re.match(r'测试开发\w', '测试开发A')
print(r.group()) # 匹配成功
r = re.match(r'测试开发\w', '测试开发班')
print(r.group()) # 匹配成功
# \W
r = re.match(r'测试开发\W', '测试开发A')
print(r.group()) # 匹配失败
r = re.match(r'测试开发\W', '测试开发\n')
print(r.group()) # 匹配成功
r = re.match(r'测试\W开发', '测试 开发')
print(r.group()) # 匹配成功
\s
和\S
# \s
r = re.match(r'测试开发\s', '测试开发\n')
print(r.group()) # 匹配成功
r = re.match(r'测试\s开发', '测试\t开发')
print(r.group()) # 匹配成功
# \S
r = re.match(r'测试\S开发', '测试#开发')
print(r.group()) # 匹配成功
[]
lst = ['测试开发a', '测试开发e', '测试开发A', '测试开发R', '测试开发5', '测试开发_']
for i in lst:
r = re.match(r'测试开发[0-9A-Za-z_]', i) # 匹配所有的字母和数字
if r:
print(r.group())
else:
print(f'{i}----匹配失败')
扩展:
[^]
:表示对[]
中的内容取反。# lst = ['测试开发a', '测试开发e', '测试开发A', '测试开发R', '测试开发5', '测试开发^'] for i in lst: r = re.match(r'测试开发[^0-9^a-z]', i) if r: print(r.group()) else: print(f'{i}----匹配失败') """ 结果: 测试开发a----匹配失败 测试开发e----匹配失败 测试开发A 测试开发R 测试开发5----匹配失败 测试开发^----匹配失败 """
.
import re
rst = re.match(r'测试开发.', '测试开发3')
print(rst.group()) # 测试开发3
# 使用 .* 匹配多行文本
str1 = """asdf
aaaaaaaaasdfasdf
asdfa
adfadsfsa
asdfasdf
asdfasdf"""
rst = re.match(r'.*', str1) # * 是匹配前一个字符出0次或无数次
print(rst.group())
''' 结果:. 不能匹配 \n
asdf
'''
# 如何使用 . 匹配\n,那么在match等方法中加入一个参数 re.S
rst = re.match(r'.*', str1, re.S) # * 是匹配前一个字符出0次或无数次
print(rst.group())
''' 结果
asdfaaaaaaaaasdfasdf
asdfa
adfadsfsa
asdfasdf
asdfasdf
'''
四、匹配多个字符
代码 | 功能 |
---|---|
* | 匹配前一个字符出现0次或者无数次,即可有可无 |
+ | 匹配前一个字符出现1次或者无数次,即至少一次 |
? | 匹配前一个字符出现0次或者1次 |
{m} | 匹配前一个字符出现m次 |
{m,n} | 匹配前一个字符出现m到n次 |
*
r = re.match(r'\d*', '0123A456789')
print(r.group())
r = re.match(r'测试.*', '测试开发57')
print(repr(r.group())) # '测试开发57'
+
r = re.match(r'\d+', '0123456789')
print(r.group())
r = re.match(r'\w+', '中国')
print(r.group())
r = re.match(r'\w+', '')
print(r.group()) # 报错。匹配失败
?
r = re.match(r'\d?', 'A567')
print(r.group()) # 匹配成功
r = re.match(r'\d?', '')
print(r.group()) # 匹配成功
{n}
r = re.match(r'\d{6}', '123456789')
print(r.group()) # 123456
r = re.match(r'\d{6}', '123')
print(r.group()) # 匹配失败
{m,n}
r = re.match(r'\d{2,5}', '123456789')
print(r.group()) # 12345
r = re.match(r'\d{2,5}', '1')
print(r.group())
- 练习:匹配座机号码
number_list = ['023-68237890', '010-68923098', '0913-68681987', '02389891235', '091398783620']
for i in number_list:
r = re.match(r'0\d{2,3}-?\d{8}', i)
if r:
print(r.group())
else:
print(f'{i}---匹配失败!')
五、匹配开头和结尾
代码 | 功能 |
---|---|
^ | 匹配字符串开头 |
$ | 匹配字符串结尾 |
^
"""
需求:匹配以一个数字开头,数字之后跟上python的字符
search():表示任意位置进行匹配
"""
lst1 = ['3python', '8ipython', 'python9', '_python', 'A6python','9python123']
for i in lst1:
r = re.search(r'^\dpython', i)
if r:
print(r.group())
else:
print(f'{i}---匹配失败..')
$
# 匹配以数字开头,并且以数字结尾,中间字符任意
lst1 = ['3python', '8ipython', 'python9', '_python2', 'A6python6','9python123', '3', '12']
for i in lst1:
r = re.search(r'^\d.*\d$', i)
if r:
print(r.group())
else:
print(f'{i}---匹配失败..')
^$
:匹配空串
r = re.search(r'^$', '')
print(repr(r.group()))
六、匹配分组
代码 | 功能 |
---|---|
| | 匹配左右任意一个表达式 |
() | 将括号中字符作为一个分组 |
\number | 引用分组number匹配 |
(?P<name>) | 分组起别名 |
(?P=name) | 引用别名为name分组匹配到的字符串 |
|
和()
''' 需求
匹配163、qq、gmail邮箱地址,邮箱用户名为6-10位
正则表达式为:[A-Za-z0-9_]{6,10}@(163|qq|gmail)\.com$
'''
import re
lst = ['liupan@163.com', 'liupan@qq.com', 'liupan@gmail']
for i in lst:
rst1 = re.match(r'[A-Za-z0-9_]{6,10}@(163|qq|gmail)\.com$', 'liupan@163.com')
if rst1:
print(rst1.group())
else:
print(f'{i}---匹配失败')
注意:邮箱中的
.
需要进行转义处理,不然在正则表达式中会当做元字符.
匹配任意单个字符!
\number
:后项引用前项
import re
str1 = '<body><h1>我是一级标题</h1></body>'
rst1 = re.match(r'<(\w+)><(\w+)>.*</(\1)></(\2)>', str1)
print(rst1.group())
(?P<name>)
和(?P=name)
import re
str1 = '<body><h1>我是一级标题</h1></body>'
rst1 = re.match(r'<(?P<p1>\w+)><(?P<p2>\w+)>.*</(?P=p2)></(?P=p1)>', str1)
print(rst1.group())
七、贪婪匹配
贪婪和非贪婪:*
、+
、{}
后面加上?
就是非贪婪。
贪婪匹配:尽可能多地匹配
非贪婪相反,表示尽可能少地匹配
import re
str3 = "woniuxywoniuxywoniuxy"
res1 = re.match("w.*y", str3) # 贪婪匹配
print(res1.group()) # woniuxywoniuxywoniuxy
res2 = re.match("w.*?y", str3) # 非贪婪匹配
print(res2.group()) # woniuxy
八、其它使用方法
search
:从字符串内开始匹配
'''
匹配出Python = 996里的数字
'''
import re
rst = re.search(r'\d+', 'Python = 996')
print(rst.group())
''' 结果
996
'''
findall
:匹配字符串中的所有满足规则的字符串,并将匹配结果以列表形式返回
import re
rst = re.findall(r'\d+', 'Python=996,java=007,C++=955')
print(rst)
''' 结果
['996', '007', '955']
'''
sub
:替换
- 使用字符串进行替换
import re
rst = re.sub(r'\d+', '123', 'Python=996,java=107,C++=955')
print(rst)
''' 结果
Python=123,java=123,C++=123
'''
- 使用函数进行替换
import re
def add_sub(tmp):
n = int(tmp.group())
n += 1
return str(n)
rst = re.sub(r'\d+', add_sub, 'Python=996,java=107,C++=955')
print(rst)
''' 结果
Python=997,java=108,C++=956
'''
split
:切割
str1 = 'info:python 996 java 007'
rst = re.split(r':| ', str1) # 不要使用()分组
print(rst)
''' 结果
['info', 'python', '996', 'java', '007']
'''
通过正则表达式进行切割,使用分组时注意,分组也会作为切割后的列表元素。