*:对前面的正则表达式匹配0到无穷次
+:对前面的正则表达式匹配1到无穷次
?:对前面的正则表达式匹配0到1次
注意:
只对前面的表达式有效,比如ab*,则只对b重复,
之所以成为贪婪模式,是因为他会尽最大可能的去匹配,能匹配10次,绝对不会匹配9次
# 例如
import re
string = 'abcddddddefg'
p = re.compile('cd*')
m = p.search(string)
print(m)
p1 = re.compile('cd?')
m1 = p1.search(string)
print(m1)
string1 = 'abcgcddddddefg'
p2 = re.compile('cd*')
p3 = re.compile('cd+')
m2 = p2.search(string1)
m3 = p3.search(string1)
print(m2)
print(m3)
# 终端显示
<re.Match object; span=(2, 9), match='cdddddd'> # *匹配cdddddd
<re.Match object; span=(2, 4), match='cd'> # ?匹配cd
<re.Match object; span=(2, 3), match='c'> # *匹配c,此时*取0
<re.Match object; span=(4, 11), match='cdddddd'> # +匹配cdddddd,因为+不能取0
注意:
这里很奇怪
*和?支持0次,所以存在一种特殊情况,空字符串
同时findall是支持空字符串的,会将空字符串作为列表元素显示出来,如果匹配不到,就会显示空字符串,空字符串的个数和匹配不到的次数+1,结尾必然有一个空字符串
# 例如
import re
string = 'bbbbb'
p = re.compile('a*')
m = p.search(string)
print(m)
string = 'bbbbb'
p = re.compile('a?')
m = p.search(string)
print(m)
string = 'bbbbb'
p = re.compile('a?')
m = p.findall(string)
print(m)
string = 'abbbbb'
p = re.compile('a?')
m = p.findall(string)
print(m)
# 终端显示
<re.Match object; span=(0, 0), match=''> # 空字符串
<re.Match object; span=(0, 0), match=''> # 空字符串
['', '', '', '', '', ''] # 一共匹配6次,结尾1次
['a', '', '', '', '', '', ''] # 一共匹配7次,结尾1次
*?:对前面的正则表达式匹配0到无穷次
+?:对前面的正则表达式匹配1到无穷次
??:对前面的正则表达式匹配0到1次
注意:
之所以成为非贪婪模式,是因为他们在能够匹配的情况下,按照最小次数去匹配
如果将上面程序统一加上?
# 例如
import re
string = 'abcddddddefg'
p = re.compile('cd*?')
m = p.search(string)
print(m)
p1 = re.compile('cd??')
m1 = p1.search(string)
print(m1)
string1 = 'abcgcddddddefg'
p2 = re.compile('cd*?')
p3 = re.compile('cd+?')
m2 = p2.search(string1)
m3 = p3.search(string1)
print(m2)
print(m3)
# 终端显示
<re.Match object; span=(2, 3), match='c'> # *?最小取0
<re.Match object; span=(2, 3), match='c'> # ??最小取0
<re.Match object; span=(2, 3), match='c'> # *?最小取0
<re.Match object; span=(4, 6), match='cd'> # +?最小取1
{m}:将前面的正则表达式重复m次;
{m, n}:将前面的正则表达式重复m-n次,尽可能多的重复,如果忽略m,则最小重复次数默认为0,如果忽略n,则最大重复次数默认无穷大,但是逗号不能忽略;
{m, n}?:非贪婪模式,将前面的正则表达式重复m-n次,尽可能少的重复;
如果m和n都不写,则表示0到无穷次,相当于*
# 例如
import re
string = 'abcddddddefg'
p = re.compile('cd{3}')
m = p.search(string)
print(m)
p1 = re.compile('cd{4,8}')
m1 = p1.search(string)
print(m1)
p1 = re.compile('cd{4,8}?')
m1 = p1.search(string)
print(m1)
string1 = 'abcgcddddddefg'
p2 = re.compile('cd{,8}')
p3 = re.compile('cd{2,}')
m2 = p2.search(string1)
m3 = p3.search(string1)
print(m2)
print(m3)
# 终端显示
<re.Match object; span=(2, 6), match='cddd'> # 重复3次
<re.Match object; span=(2, 9), match='cdddddd'> # 4-8次尽可能多的匹配
<re.Match object; span=(2, 7), match='cdddd'> # 4-8次尽可能少的匹配
<re.Match object; span=(2, 3), match='c'> # 没有m默认为0
<re.Match object; span=(4, 11), match='cdddddd'> # 没有n默认无穷大
注意:
在正则表达式中,不要随便加空格,会导致结果改变
# 例如
import re
string = 'ab{3, 5}'
p = re.compile('ab{3, 5}')
m = p.search(string)
print(m)
# 终端显示
<re.Match object; span=(0, 8), match='ab{3, 5}'> #此时是全匹配,因为在ab{3, 5}中5的前面加了一个空格,他就是正常的字符串,不是特殊字符
|:任意个正则表达式用|连接,则表示A|B,即A或B,当有一个成功的时候,另外一个则不再匹配
# 例如
import re
string = 'abcddddddefg'
p = re.compile('cd{0}|cd{3}')
m = p.search(string)
print(m)
# 终端显示
<re.Match object; span=(2, 3), match='c'> # 在匹配到cd{0}之后,就不在匹配另外一个{3}l ,可以看出来,他是从左往右执行
# import re
string = 'abcddddddefg'
p = re.compile('cd{7}|cd{3}')
m = p.search(string)
print(m)
# 终端显示
<re.Match object; span=(2, 6), match='cddd'> #左边不符合要求则匹配右边
# 例如
import re
string = 'abcddddddefg'
p = re.compile('cd{7}|cd{8}')
m = p.search(string)
print(m)
# 终端显示
None # 两边都不符合则返回None
\:1、转义特殊字符串;2、与字符形成特殊序列
# 例如
import re
string = 'abc.'
p = re.compile('.')
m = p.search(string)
print(m)
string = 'abc.'
p = re.compile('\.')
m = p.search(string)
print(m)
# 终端显示
<re.Match object; span=(0, 1), match='a'> # 转义之前.会表示任一非换行符的字符串
<re.Match object; span=(3, 4), match='.'> # 转义之后,只会匹配
1、\A:匹配开头,在re.M情况下,不会判断每行的开始
# 例如
import re
string = 'abcdefg'
p = re.compile('\Aa')
m = p.search(string)
print(m)
import re
string = 'abcd\nefg'
p = re.compile('\Aa',flags=re.M)
m = p.findall(string)
print(m)
# 终端显示
<re.Match object; span=(0, 1), match='a'>
['a'] # re.M模式也一样
2、\b:匹配空字符串,但是只会在单词边界(空格,换行,制表)的开头或结尾位置
# 例如
import re
string = 'abcdefg'
p = re.compile(r'g\b')
m = p.search(string)
print(m)
string = 'abcd e fg'
p = re.compile(r'\be\b')
m = p.search(string)
print(m)
# 终端显示
<re.Match object; span=(6, 7), match='g'> # 相当于匹配结尾
<re.Match object; span=(5, 6), match='e'> # 匹配中间隔开的e
3、\B:匹配非单词边界的空字符串
# 例如
import re
string = 'abcdefg'
p = re.compile(r'g\B')
m = p.search(string)
print(m)
string = 'abcd e fg'
p = re.compile(r'\Bb\B')
m = p.search(string)
print(m)
# 终端显示
None # 发现结尾的g匹配不到了
<re.Match object; span=(1, 2), match='b'> #可匹配到中间的b
4、\d:任意一个十进制的数,即(0-9)
# 例如
import re
string = '123456789abc'
p = re.compile(r'\d')
m = p.search(string)
print(m)
# 终端显示
<re.Match object; span=(0, 1), match='1'> # 匹配到1
5、\D:任意一个非数字字符
# 例如
import re
string = '123456789abc'
p = re.compile(r'\D')
m = p.search(string)
print(m)
# 终端显示
<re.Match object; span=(9, 10), match='a'> # 匹配到a
6、\s:任意一个空白字符(空格,换行,制表)
# 例如
import re
string = 'adb a bcd'
p = re.compile(r'a\sb')
m = p.search(string)
print(m)
# 终端显示
<re.Match object; span=(4, 7), match='a b'>
7、\S:任意一个非空白字符
# 例如
import re
string = 'adb a bcd'
p = re.compile(r'a\Sb')
m = p.search(string)
print(m)
# 终端显示
<re.Match object; span=(0, 3), match='adb'>
8、\w:一个字母或数字或下划线
# 例如
import re
string = '123_a 56_'
p = re.compile(r'\w*')
m = p.findall(string)
print(m)
# 终端显示
['123_a', '', '56_', ''] # 可以看到中间空格部分输出是空字符串
9、\W:一个非字母数字或下划线
# 例如
import re
string = '123_a 56_'
p = re.compile(r'\W*')
m = p.findall(string)
print(m)
# 终端显示
['', '', '', '', '', ' ', '', '', '', ''] # 可以看到除了中间空格外,都是空字符串
10、\Z:只匹配字符串的结尾,在re.M情况下,不会判断每个换行符的前面
# 例如
import re
string = 'abcdefg'
p = re.compile('g\Z')
m = p.search(string)
print(m)
string = 'abcd\nefg'
p = re.compile('d\Z', flags=re.M)
m = p.findall(string)
print(m)
# 终端显示
<re.Match object; span=(6, 7), match='g'>
[] # re.M模式下也一样
只有\Z的时候,匹配一个空字符串
# 例如
import re
string = 'abcdefg'
p = re.compile('\Z')
m = p.findall(string)
print(m)
# 终端显示
['']
[]:字符集
[amk]等价于’a’|‘m’|‘k’
[a-y]:匹配a到y之间的小写字母
[0-5]:匹配0到5之间的数字
[0-5][a-y]:匹配两个字符
[.+]:特殊字符在字符集中会失去特殊含义,只能代表普通字符,相当于转义,但是斜杠加字母的特殊序列可以被接受
[]:在[]中,会表示取反
# 例如
import re
string = '123_abc'
p = re.compile('[^_]')
m = p.findall(string)
print(m)
# 终端显示
['1', '2', '3', 'a', 'b', 'c'] # ^_表示除了_以外的字符
注意:
# 例如
import re
string = '[]'
p = re.compile('[[]]')
m = p.findall(string)
print(m)
import re
string = '[]'
p = re.compile('\[\]')
m = p.findall(string)
print(m)
import re
string = '[]'
p = re.compile('[\[\]]')
m = p.findall(string)
print(m)
# 终端显示
['[]'] #和一起的
['[]'] # 和一起的
['[', ']'] # 在方括号[]里面,所以显示也是分开的
# 例如
import re
p = re.compile('b.+a.+e')
m = p.match('babacdefg')
print(m)
p = re.compile('b(.+)a(.+)e')
m = p.match('babacdefg')
print(m.group(1)) # 获取1组的内容
print(m.group(1,2)) # 1组和2组的内容
print(m.groups()) #groups返回所有子组结果
# 终端显示
<re.Match object; span=(0, 7), match='babacde'>
ab
('ab', 'cd') # 结果是元组形式
('ab', 'cd') # 显示所有子组
groups(default = None):当返回的子组没有参与匹配的时候,默认返回None,同时可以捕获所有子组结果,结果以元组的形式呈现
start(groups):返回该组起始位置,当该组没有匹配的时候返回-1
end(groups):返回该组结束位置,左闭右开,当该组没有匹配的时候返回-1
span(groups):返回该组索引,左闭右开,相当于把start和end结合起来
组如果嵌套起来,0组等于1组
# 例如
import re
p = re.compile('b(.+)(.+)?(.+)?')
m = p.match('babacdefg')
print(m.group(1))
print(m.group(1, 2))
print(m.groups('NO'))
print(m.start(1))
print(m.start(2))
print(m.end(1))
print(m.end(3))
print(m.span(1))
print(m.span(2))
# 终端显示
abacdefg
('abacdefg', None)
('abacdefg', 'NO', 'NO')
1 # start可以传组,返回该组起始位置
-1 # 当该组没有匹配的时候返回-1
9
-1
(1, 9)
(-1, -1)
# 例如
import re
p = re.compile('(b(.+)(.+)?(.+)?)')
m = p.match('babacdefg')
print(m.group(1))
print(m.group(0))
print(m.group(2))
# 终端显示
babacdefg
babacdefg
abacdefg
后面加\数字表示和前面一样的重复次数
# 例如
import re
p = re.compile(r'(.+) \1')
m = p.match('bab babcdefg')
print(m)
p = re.compile(r'(.+) (.+)')
m = p.match('bab babcdefg')
print(m)
# 终端显示
<re.Match object; span=(0, 7), match='bab bab'>
<re.Match object; span=(0, 12), match='bab babcdefg'>
如果有捕获分组的时候,findall只会捕获分组里面的内容
# 例如
import re
p = re.compile('b(.+)a(.+)e')
m = p.findall('babacdefg')
print(m)
# 终端显示
[('ab', 'cd')]
匹配()可以加\或者放到[]中去
(?:…):非捕获分组,非捕获分组不会形成子组
# 例如
import re
p = re.compile('b(?:.+)a(.+)e')
m = p.findall('babacdefg')
print(m)
m = p.match('babacdefg')
print(m.groups())
print(m.group(1))
# 终端显示
['cd'] # 显示只有一个组
('cd',)
cd
(?=…):前向肯定界定符,不参与匹配结果,只参与匹配判定,如果判定为真,则继续,如果判定失败,则返回None
# 例如
import re
p = re.compile('.*[.](?=exe$).+') # 判定是否以exe结尾,如果是,继续进行,不是,返回空
m = p.findall('abc.exe')
print(m)
p = re.compile('.*[.](?=exe$)')
m = p.findall('abc.exe')
print(m)
p = re.compile('.*[.](?=exe$)')
m = p.findall('abc.exa')
print(m)
# 终端显示
['abc.exe'] # 只参与判定,不参与匹配
['abc.'] # 你看,如果去掉最后的.+就停在了abc.,因为后面没有了
[] # 判定失败
(?!…):前向否定界定符,不参与匹配结果,只参与匹配判定,如果判定为假,则继续,如果判定为真,则返回None
# 例如
import re
p = re.compile('.*[.](?!exe$).+') # 匹配成功,返回空
m = p.findall('abc.exe')
print(m)
p = re.compile('.*[.](?!exe$)')
m = p.findall('abc.exe')
print(m)
p = re.compile('.*[.](?!exe$).+') # 匹配失败,继续往下
m = p.findall('abc.exa')
print(m)
# 终端显示
[]
[]
['abc.exa']
p.split(string, maxsplit = 0):用子字符串(就是compile里面的字符串)把字符串string进行分割,以列表形式返回,maxsplit = 0最大分割次数,默认为0
# 例如
import re
p = re.compile('\W+')
m = p.split('word, word, word.')
print(m)
p = re.compile('(\W+)') # 有捕获分组
m = p.split('word, word, word.')
print(m)
p = re.compile('\w+') # 开头就是一刀
m = p.split('word, word, word.')
print(m)
# 终端显示
['word', 'word', 'word', '']
['word', ', ', 'word', ', ', 'word', '.', ''] # 有捕获分组,分组内容也显示
['', ', ', ', ', '.'] # 开头有一刀的话,在最开始有一个''
p.sub(repl, string, count=0):替换,将repl替换掉string中的子字符串(就是compile)
第一个repl可以是字符串,还可以是函数
# 例如
import re
p = re.compile(r'blue|white|red')
print(p.sub('colour', 'blue socks and red shoes'))
print(p.sub('colour', 'blue socks and red shoes',count=1))
# 终端显示
colour socks and colour shoes # 先匹配,然后将匹配到的东西替换掉
colour socks and red shoes # 限制替换次数
当repl是函数
# 例如
import re
def func(matchobj):
if matchobj.group(0) == '-':
return ' '
else:
return '-'
p = re.compile(r'-{1,2}') # 先匹配,将匹配到的字符串作为参数,返回一个match对象,将match对象传入函数中,然后执行函数体,将返回的结果替换掉原字符串的相关东西,注意匹配的时候是非重叠对象
print(p.sub(func, 'pro----gram-files'))
# 终端显示
pro--gram files
p.subn(repl, string, count=0):替换,将repl替换掉string中的子字符串(就是compile)
第一个repl可以是字符串,还可以是函数,多返回一个替换次数,以元组形式返回
# 例如
import re
def func(matchobj):
if matchobj.group(0) == '-':
return ' '
else:
return '-'
p = re.compile(r'-{1,2}')
print(p.subn(func, 'pro----gram-files'))
# 终端显示
('pro--gram files', 3)
re.对象方法.(编译内容,string,flags = 0)
形式简单,但是占用内存,省去了编译的过程
# 例如
import re
def func(matchobj):
if matchobj.group(0) == '-':
return ' '
else:
return '-'
p = re.compile(r'-{1,2}')
print(p.subn(func, 'pro----gram-files'))
print(re.subn(r'-{1,2}', func, 'pro----gram-files'))
# 终端显示
('pro--gram files', 3)
('pro--gram files', 3) # 可以看到结果一致
正则表达式可以加空白符(空格,换行,制表)
# 例如
import re
# 等价于 p = re.compile(r"\d+\.\d*")
p = re.compile(r"""
\d + # 匹配整数部分, re.X使得该空格不影响
\. # 匹配小数点, re.X使得可以分段写
\d * # 匹配小数部分, re.X使得该空格不影响
""", re.X)
print(p.findall("1.2345.78")) # 换行用三引号
# 终端显示
['1.2345'] # 不影响结果