版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
正则表达式
Talk is cheap. Show me the code.
import re
#注:介绍方法过后,==>是对参数的解释
#re: 查找母串是否含某子串,母串中提取子串,匹配的子串进行替换操作
'''正则表达式:一种特殊规则的字符串,特殊之处在二:一是,它是按照某种规则进行的
,可能不只有一个值。二是,它有对应的方法,match。换句话说,正则表达式就是一个用于匹配字符串的模板'''
'''上面这种预编译用到正则表达式对象,更加强大。'''
print('第零部分:这只是个示例')
#预编译正则表达式(预编译的好处是,可以多次调用)
#re.compile('string') ==>子串
#p是.pattern对象的实例,是一个缓存
p = re.compile('abc')
#调用这个实例的方法search('string')==>母串
a = p.search('www.abc.com')
print(a)#可以看到 <_sre.SRE_Match object; span=(4, 7), match='abc'>
#span()返回匹配的位置,group()返回匹配的组 如下两行:
print(a.span())
print(a.group())
#不使用预编译的话,上面也可以这样写
a = re.search('abc','www.abc.com')
print('\n第一部分:只返回一处 match 和 search ')
#re.match(pattern,string,flags=0) ==>正则,母串,旗标
#match:从开始位置匹配,不成功返回none,成功则返回一个Match对象
m1 = re.match('www','www.fkit.org')
print(m1)
print(m1.span())
print(m1.group())
#re.search(pattern,string,flags=0) ==>正则,母串,旗标
#search:扫描整个,不成功返回none,成功返回match对象
m2 = re.search('fkit','www.fkit.org')
print(m2)
print(m2.span())
print(m2.group())
print('\n第二部分:返回多处 findall 和 finditer ')
#re.findall(pattern,string,flags=0) ==>正则,母串,旗标
#findall:扫描整个,返回所有匹配正则的子串的列表
m3 = re.findall('hello','hello world,hello python')
print(m3)
#re.finditer(pattern,string,flags=0) ==>正则,母串,旗标
#finditer:扫描整个,返回所有匹配正则的子串的迭代器
m4 = re.finditer('hello','hello world,hello python')
for m4_ in m4:
print(m4_)#会打印两个match对象
print('\n第三部分:全匹配与替换 fullmatch 和 sub ')
#re.fullmatch(pattern,string,flags=0) ==>正则,母串,旗标
#fullmatch:扫描整个,要求母串等于子串,不等返回none,等于返回match
m5 = re.fullmatch('hello','hello world,hello python')
print(m5)
#re.sub(pattern,repl,string,count=0,flags=0)
# ==>正则,用于替换的,母串,替换次数(为零则全换),旗标
#sub:扫描整个,要求母串等于子串,不等返回none,等于返回match
m6 = re.sub('-','/','2019-10-8')
print(m6)#此时为全换,因为count未指定参数
m7 = re.sub('-','/','2019-10-8',1)
print(m7)#只替换一次
print('\n第四部分:所执行的替换,基于被替换内容进行改变')
def fun_1(matched):
#matched就是匹配对象
s1 = '《hello' + (matched.group('lang')) + '》'
#括号内的底层逻辑是: 子串.组(命名为lang的组)
return s1
s2 = 'world是一本书,python也是一本书'
'''r'(?P<lang>\w+)' 正则表达式。r表示不理会里面的特殊字符,即原始字符串。
并用 ?P<lang>取名为 lang; \w+才是正则表达式内容,\W表示任意字符,+表示
可出现一次到多次 ; \W+表示一个或多个任意字符'''
m8 = re.sub(r'(?P<lang>\w+)',fun_1,s2,flags=re.A)#旗标re.A,表示ASCII字符,不能代表汉字
#也就是把每一段ASCII编码的放进去
'''期间的逻辑是:一个正则表达式子串(用特殊规则在母串中截取的片段),替换
值是一个函数的返回值,表示选取名为'lang'的组中的值,即这个子串。'''
print(m8)
print('\n第五部分:分割 split')
'''re.split(pattern,string,maxsplit=0,flags=0)
==>正则,母串,最多分割数,旗标
split:re.split() 基本等同于 string.split(), 返回分割后的列表'''
m9 = re.split('and','apple and banana and orange')
m10 = 'apple and banana and orange'.split('and')
print(m9)
print(m10)
#指定只分割一次
m11 = re.split('and','apple and banana and orange',1)
print(m11)
print('\n第六部分:清除正则表达式缓存(也就是最上面的p),添加转义字符')
re.purge()
m12 = re.escape(r'www.study.cn')
print(m12)
'''re 模块中还包含两个类,一个是正则对象 pattern ,一个是匹配对象 match
正则表达式对象就是 re.compile 的返回值。该对象的方法与前面 re 模块中的函数大致对应
正则表达式对象的这些方法更加强大,可以额外指定 pos 和 endpos 两个参数,用于指定处理
一个区间的子串。'''
print('\n第七部分:正则表达式的方法来执行匹配')
#编译得到正则表达式对象
q1 = re.compile('fkit')
#调用 match 方法,原本应该从开始位置匹配
n1 = q1.match('www.fkit.org',4)#从4开始
print(n1.span())#(4,8),读取的索引还是相对于母串的索引
n2 = q1.match('www.fkit.org',4,8)#从4开始,从8结束
print(n2.span())
print('\n第八部分:正则表达式中使用组')
m13 = re.search(r'(fkit).(org)',r'www.fkit.org is a good domain')
#调用的简化写法,0表示所有组,group返回一个字符串
print(m13.group(0))# fkit.org
print(m13[0])# fkit.org
print(m13.span(0))# (4,12)
#调用的简化写法,1表示第一组
print(m13.group(1))# fkit
print(m13[1])# fkit
print(m13.span(1))# (4,8)
#调用的简化写法,2表示第二组
print(m13.group(2))# org
print(m13[2])# org
print(m13.span(2))# (9,12)
#返回所有组所匹配的字符串组成的元组
print(m13.groups())# ('fkit', 'org')
'''只要正则表达式能匹配得到结果,不管它是否包含组。group(0)总能获得所匹配的
子串,span(0)总能获得开始结束位置'''
'''如果在正则表达式中为组指定了名字 (?P<名字>一个子串),对比第四部分看'''
print('\n第九部分:获取所有组名字的匹配所组成的字典 groupdict')
#
m14 = re.search(r'(?P<one>fkit).(?P<two>org)',r'www.fkit.org is a good domain')
print(m14.groupdict())#{'one': 'fkit', 'two': 'org'}
#键为 组名 ,值为 子串 。
print('\n第十部分:正则表达式旗标')
# re.A 或re.ASCII: 控制只匹配ASCII值。行内旗标(?a)
# re.DEBUG: 显示编译正则表达式的Debug信息。没有行内旗标
m15 = re.findall('\w+','hello 世界,hello 你好')
print(m15)#['hello', '世界', 'hello', '你好']==>按照字符节点划分
m16 = re.findall('\w+','hello 世界,hello 你好',re.A)
print(m16)#['hello', 'hello']
# re.I 或re.IGNORECASE: 使用正则表达式匹配时,不区分大小写。行内旗标(?i)
#默认区分大小写,所以无匹配
m17 = re.findall('Fkit','fkit is a good domain,FKIT is good')
print(m17)#[]
m18 = re.findall('Fkit','fkit is a good domain,FKIT is good',re.I)
print(m18)#['fkit', 'FKIT']
# re.L 或re.LOCALE: 使用正则表达式不区分大小写,只对bytes模式起作用。行内旗标(?L)
# re.S 或s.DOTALL: 让 . 能匹配包括换行符在内所有字符,如果无此旗标。 . 能匹配不包括换行符的所有字符。行内旗标(?s)
str_1 = '''hellohithanks
hipass'''
m19 = re.findall('hello(.*?)pass',str_1)
m20 = re.findall('hello(.*?)pass',str_1,re.S)
print(m19)#[]
print(m20)#['hithanks\nhi'],结尾一定要跟字符,比如会出现['']
# re.M 或re.MULTILINE: 多行模式, ^ 可以匹配字符串的开头和每行的开头, $ 可以匹配字
# 符串的末尾和每行的末尾(在换行符前后)。行内旗标(?m)
str_2 = '''一个 两个
三个 小朋友
一起 手拉手 玩雪球'''
m21 = re.findall(r'^\w+',str_2,re.M)#匹配行开头
m22 = re.findall(r'\w+$',str_2,re.M)#匹配行结尾
print(m21)#['一个', '三个', '一起']
print(m22)#['两个', '小朋友', '玩雪球']
h1 = re.findall(r'\A\w+',str_2,re.M)#匹配字符串开头
h2 = re.findall(r'\w+\Z',str_2,re.M)#匹配字符串结尾
h3 = re.findall(r'\b\w+',str_2,re.M)#单词的边界
h4 = re.findall(r'\B\w+',str_2,re.M)#非单词的边界
print(h1)#['一个']
print(h2)#['玩雪球']
print(h3)#['一个', '两个', '三个', '小朋友', '一起', '手拉手', '玩雪球']
print(h4)#['个', '个', '个', '朋友', '起', '拉手', '雪球']
# re.U 或re.Unicode: 多余的。
# re.X 或re.VERBOSE: 允许分行书写正则表达式,并可添加注释。行内旗标(?x)
m23_0 = re.compile(r'''2020#年
6#月
18#日''',re.X)
print(m23_0)#re.compile('2020#年\n6#月\n18#日', re.VERBOSE)
print('\n第十一部分:正则表达式中各种字符')
#正则表达式中合法字符
r''' \t制表符
\n换行符
\r回车符
\f换页符
\a报警符
\cx:比如Ctrl+V==>\cv'''
#正则表达式中特殊字符
r'''$匹配一行的结尾
^匹配一行的开头
()标记子表达式(也就是组)的开始位置和结束位置
[]用于确定中括号表达式的开始位置和结束位置
{}用于标记前面子表达式的出现频度
*指定前面子表达式可以出现零次或多次
+指定前面子表达式可以出现一次或多次
?指定前面子表达式可以出现零次或一次
.匹配除\n以外的容易字符
\转义下一个字符
|在两项之间任选一项'''
#正则表达式所支持的预定义字符
r'''\d匹配数字
\s匹配所有空白字符
\w匹配所有的单词字符
它们的大写与它们正好相反,\D\S\W'''
m24 = re.findall(r'c\wt','cat cot cet')
print(m24)
m25 = re.findall(r'\d\d\d-\d\d\d\d-\d\d\d','123-1234-1234')
print(m25)
#中括号表达式
r'''[abc],表示枚举abc中任何一个字符
[a-f],表示a-f范围内任意字符
[a-cx-z],枚举和范围结合,表示a-c,x-z范围内任意字符
^表示非,[^abc]表示除abc外的字符。[^a-f]表示除a-f外所有字符'''
#边界匹配符:看第十部分
r'''^行开头
$行结尾
\b单词的边界,即只能匹配单词前后的空白
\B非单词的边界,即只能匹配不在单词前后的空白
\A只匹配字符串的开头
\Z只匹配字符串的结尾,仅用于最后的结尾'''
print('\n第十二部分:子表达式(圆括号表达式)')
#(子表达式): 匹配子表达式,并捕获成一个自动命名的组,后面可通过“\1”引用第一个捕获的组。
str_3='windows 98 publishedin 98'
#后面publishedin连在一起,对应子串里的\W+,空格对应空格,\1表示
m26 = re.search(r'windows (95|98|NT|20) \w+ \1',str_3)
print(m26)
#(?P<组名>子表达式): 捕获成为命名组。用(?P=组名调用)
str_4 = r'<view>这是一个视窗</view>'
m27 = re.search(r'<(?P<V>\w+)>\w+</(?P=V)>',str_4)#如果是findall,就是这个['view']
#findall返回所有满足pattern子串返回的列表。当有子表达式时,返回的是子串的子串形成的列表
print(m27)#<_sre.SRE_Match object; span=(0, 19), match='<view>这是一个视窗</view>'>
#(?:子表达式): 匹配子表达式,但是不捕获
m28 = re.search(r'windows (\d*) \w+',str_3)
print(m28)#<_sre.SRE_Match object; span=(0, 25), match='windows 98 publishedin 98'>
#(?<=子表达式):子表达式必须在匹配内容左侧,但该子表达式不作为匹配的一部分
#(?=子表达式):子表达式必须在匹配内容右侧,但该子表达式不作为匹配的一部分
str_5 = '<html><body><h1>号外!号外!大新闻!</h1></body></html>'
m29 = re.search(r'(?<=<h1>).+?(?=</h1>)',str_5)
print(m29)#<_sre.SRE_Match object; span=(16, 26), match='号外!号外!大新闻!'>
#(?<!子表达式):子表达式必须不在匹配内容左侧,并且该子表达式不作为匹配的一部分
#(?!子表达式):子表达式必须不在匹配内容右侧,并且该子表达式不作为匹配的一部分
#(?#注释的内容):注释组。不影响正则表达式本身
str_6 = 'Www.Baidu.Com'
m30 = re.search(r'[A-Za-z0-9.]{3,}(?#前面子表达式的内容要出现三次及以上)',str_6)
print(m30)#<_sre.SRE_Match object; span=(0, 13), match='Www.Baidu.Com'>
#(?旗标名):旗标组,用于为!!!整个!!!正则表达式添加行内旗标
m31 = re.findall(r'(?i)[A-Z0-9.]+',str_6)#['Www.Baidu.Com']
print(m31)#['Www.Baidu.Com']
#(?旗标名:表达式):只对当前组起作用的旗标
m32 = re.findall(r'(?i:[a-z0-9.]+)Baidu.com',str_6)#['Www.Baidu.Com']
print(m32)#[]
#在旗标前用 - 表示去掉该旗标
m33 = re.search(r'(?-i:[a-z0-9.]+)Baidu.Com',str_6,re.I)
print(m33)#<_sre.SRE_Match object; span=(1, 13), match='ww.Baidu.Com'>
print('\n第十三部分:贪婪模式与勉强模式')
#频度限定,限定前面的子表达式出现的次数
r'''* : 0~N次,等价于 {0,}
+ : 1~N次,等价于 {1,}
? : 0~1次,等价于 {0,1}
{n,m} : n~m次,n 和 m 要为非负整数。
{n,} : 至少出现n次
{,m} : 最多出现m次
{n} : 必须出现n次'''
str_7 = '[email protected].'
#贪婪模式:会尽可能多的匹配,所以匹配到了最末尾的点
m34 = re.search(r'@.+\.',str_7)
print(m34)#<_sre.SRE_Match object; span=(3, 12), match='@efg.com.'>
#勉强模式:尽可能少的匹配。方法是在频度后加 ?
m35 = re.search(r'@.+?\.',str_7)
print(m35)#<_sre.SRE_Match object; span=(3, 8), match='@efg.'>