正则表达式
应用场景
- 特定规律字符串的查找,切割,替换
- 邮箱格式:URl,IP地址等的校验
- 爬虫项目中,特定内容的提取
使用原则
- 只要使用字符串等函数能解决的问题,就不要使用正则
- 正则的效率比较低,同时会降低代码的可读性
- 世界上最难理解的三样东西,医生的处方,道士的神符,码农的正则
- 提醒: 正则是用来写的,不是用来读的,在不指定功能的情况下,不要试图阅读别人的正则
基本使用
- 说明: 正则不是我们写的,python中正则的解析通过 re模块完成
- 相关函数;
- match: 从开头进行匹配,匹配到就返回正则结果对象,没有返回None
- sertch:从任意位置匹配,功能同上
import re
# m = re.match("abc","adask;jabcsdaj1")
m1 = re.match("abc","abcsk;jadasdaj1")
# print(m) # 没有找到返回None
# print(m1)# 找到正则匹配结果对象,
if m1:
print(m1.group()) # 返回匹配的内容
print(m1.span()) #返回匹配的内容位置
"""
# match 从开头查找
# """
#
s = re.search("abc","adask;jabcsdaj1d")
# print(s)
- findall : 有就返回所有匹配对象,组成列表,没有返回空列表
# 匹配所有内容,找到返回所有匹配的内容列表,
# 没有找到返回空列表
f = re.findall("abcd","adask;jabcsdabcaj1")
# print(f)
- compile : 生成正则表达式对象
# 先生成正则表达式对象
c = re.compile("hello")
# print(type(c))
# 从开头匹配
i = c.match("sad;asdawafhelloasdaw;asdw")
print(i)
# 从任意位置匹配
se = c.search("sasdwdhelloasd;asdas")
print(se)
# 匹配所有内容
fi = c.findall("helloadad;asdahello;asdfkajehelloasda")
print(fi)
- 说明 ,此方式更加灵活,将正则匹配分为两步完成(先生成正则对象,然后需要时进行验证)
正则规则
- 单个字符
- "普通字符:简单来说就是一对一的完全匹配"
[]:中间的任意一个字符
[a-z]: a~z之间的任意字符(所有的小写字母)
[A-Za-z]:匹配所有的字母,多个连续的片段中间不能有任何多余的字符
[^0-9]: 匹配 除了0-9 以外的所有字符
. (点): 匹配除 \n 以外的任意字符
自行测试:
\d :数字字符 ,相当于[0-9]
\D :非数字字符 ,相当于[^0-9]
\w :匹配字(数字,字母,下划线,汉字)
\W :匹配非字,(\w相反的内容)
\s :所有的空白字符
\S :所有的非空白字符
\b :词边界 (开头。结尾,空格,标点等)
\B :非词边界(\b相反的内容)
- 次数限定:
- # 指定字符任意次
# f = re.findall("ab*c","sabfabibabuabcbbavadba")
# 指定字符至少一次
print(re.findall("ab+c","sdhasjhfsljabcasofjia"))
# 指定字符最多出现一次,有或者没有都可以
print(re.findall("ab?c","sdhasjhfsljacasofjia"))
# #指定字符出现指定次数
print(re.findall("ab{3}c","sdhasjhabbbcfsljacasofjia"))
# 指定字符 出现次数的范围
print(re.findall("ab{1,3}c","sdhabbbcsjhabcfslfjia"))
# 指定字符,至多出现多少次
# print(re.findall("ab{0,5}c","sacaabcabcabcabia"))
# 指定字符,至少出现多少次
print(re.findall("ab{3,}c","sdhabbchfsljaca"))
*:前面的字符出现任意次
+:至少一次
?:最多一次
{m,n}:m <= 次数 <= n
{m,}:至少m次
{,n}:至多n次
{m}:指定m次
- 边界限定
- ^ : 以指定内容从开头进行匹配
- $ : 以指定内容从结尾进行匹配-p
import re
# ^ : 以指定内容从开头进行匹配
print( re.findall("^hello","adahelloaawhellowadhello"))
# $ : 以指定内容从结尾进行匹配
print( re.findall("hello$","adahelloaawhellowadhellxo"))
- 优先级控制
- | 表示或,它拥有最低的优先级
- () 表示一个整体,可以明确的指定结合性/优先级
import re
print(re.findall("a(hello|world)c","hiahellociaworldc")
- 分组匹配
c = re.compile(r"(\d+)([a-z]+)(\d+)")
s = c.search("sadasdaf454as6aa46sd4w")
"""
完整匹配结果
print(s.group())
↑↓等价
print(s.group(0))
"""
# 第几个()匹配的结果
print(s.group(1), s.span(1))
print(s.group(2), s.span(2))
print(s.group(3), s.span(3))
- 示例2:
import re
# 固定匹配
# c = re.compile(r'<a>\w+</a>')
# \1表示前面第一个()匹配的内容
# c = re.compile(r'<([a-z]+)><([a-z]+)>\w+</\2></\1>')
# 给分组()起名字
c = re.compile(r'<(?P<goudan>[a-z]+)><(?P<dahua>[a-z]+)>\w+</(?P=dahua)></(?P=goudan)>')
s = c.search('<div><a>百度一下</a></div>')
if s:
print(s.group())
- 贪婪匹配
- 贪婪 : 最大限度的匹配,正则的匹配默认就是贪婪。
- 非贪婪: 只要满足条件, 能少匹配就少匹配。? 经常用于取消贪婪
import re
# ? :取消任意多次的贪婪匹配
# c = re.compile(r"a.*?b")
# s = c.search("wwa0a0dwaadavab")
# ?:取消至少一次的贪婪匹配
c = re.compile(r"a.+?b")
s = c.search("wwa0abwaadavab")
if s:
print(s.group())
- 匹配模式
- 说明: 匹配模式就是匹配的原则进行模式的修正
import re
"""
flags: 标志位,用于控制正则表达式的匹配方式,值如下:
re.I 忽略大小写(常用)
re.L 做本地户识别
re.M 多行匹配,影响^和$(常用)
re.S 是. 匹配包括换行符 \n 在内的所有字符(常用)
re.U 根据Unicode字符集解析字符,影响 r"\w \W \b \B"(常用)
re.X 使我们以更灵活的格式理解正则表达式 (常用)
"""
# 忽略大小写re.I
s = re.search(r'hello', 'HELLO world', re.I)
# 正则默认是单行匹配,使用 re.M 可以进行多行匹配
s = re.search(r'^hello', 'sdhasdh\nhello world', re.M)
# 使.匹配任意字符,作为单行处理,忽略\n
# string = '<div>hello</div>'
string = '''<div>
hello
</div>'''
s = re.search(r'<div>.*?</div>', string, re.S)
if s:
print(s.group())
- findall
import re
# 没有(),结果集中保存完整匹配的内容
# f = re.findall('abc', 'abcdasabcakdjklasjabc')
# 与上面的情况等价
# f = re.findall('(abc)', 'abcdasabcakdjklasjabc')
# 按照正则进行完整匹配,但是结果集只显示分组内容
# f = re.findall('qq(abc)ww', 'ahdakqqabcwwhsad')
# 按照正则进行完整匹配,但是结果集中的元素时分组信息组成的元组
# f = re.findall('qq(abc)(ww)', 'ahdakqqabcwwhsaqqabcwwd')
# 满足上面所有的规律
# f = re.findall('(qq(abc)(ww))', 'ahdakqqabcwwhsaqqabcwwd')
# 匹配:'a(hello)c' 或 'a(world)c'
# f = re.findall('a(hello|world)c', 'sadahellocshdjaworldcsd')
f = re.findall('(a(hello|world)c)', 'sadahellocshdjaworldcsd')
print(f)
- split
import re
c = re.compile(r'\d')
s = '正则其实不难1但是学完之后2发现写不出来3是这样吧'
# 按照正则规律进行切割,可以指定切割次数
ret = c.split(s, 1)
print(ret)
- sub
import re
'''
c = re.compile(r'\d')
# 使用特点内容替换正则匹配到的内容,可以指定替换次数
ret = c.sub(' ', 'how1are2you', 2)
print(ret)
'''
def double(s):
# print(s)
return str(int(s.group()) * 2)
# 可以人为的干预正则替换的过程
# 因此需要传递一个处理的(匿名)函数
ret = re.sub(r'\d', double, 'how1are2you')
print(ret)
- 转义问题
import re
# 匹配所有正则语法中涉及的字符都需要转义
'''
# python: \\\\d \\\d r'\\d'
# re: \\d \\d \\d
# 匹配: \d \d \d
# c = re.compile('\\\\d')
# c = re.compile('\\\d')
# 推荐使用,否则你需要多写好多\
c = re.compile(r'\\d')
s = c.search('\d')
if s:
print(s.group())
'''
'''
c = re.compile(r'\\b')
# 在匹配的字符串中有特殊意义的字符也需要进行转义
# 若不想转义最好前面添加字符'r'
s = c.search(r'\b')
if s:
print(s.group())
'''
# 了解
# c = re.compile(r'\bhello')
# 单独匹配边界,findall保存的结果全都是空白
c = re.compile(r'\b')
f = c.findall('hello,sdjaks')
print(f)
1. 使用正则时,python字符串有特殊意义的字符最好转义
2. 要匹配在正则语法中有特殊函数的字符,都需要进行转义
3. 字符串前添加'r',可以使其变为原始字符,不要考虑转义问题