python正则-- re模块
匹配数字相关
'.' 默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行
'^' 匹配字符开头,若指定flags MULTILINE,这种也可以匹配上(r"^a","\nabc\neee",flags=re.MULTILINE)
'$' 匹配字符结尾,或e.search("foo$","bfoo\nsdfsf",flags=re.MULTILINE).group()也可以
'*' 匹配*号前的字符0次或多次,re.findall("ab*","cabb3abcbbac") 结果为['abb', 'ab', 'a'],如果想控制一个组,可以加()
'+' 匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb']
'?' 匹配前一个字符1次或0次,常用来表示可有可无(出现1次或0次)的一个符号
'{m}' 匹配前一个字符m次
'{n,m}' 匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb'] ,可以替代'*' '+' '?' 为:{0,} {1,} {0,1}
'[]' 字符集,匹配[]中的字符(其中的字符是或的关系),其中的字符可以一一列出,也可以给出范围,元字符在其中失去意义,除非元字符前面加了反斜杠。有几种特殊的元字符依然有意义:表示范围的'-', 取反的'^',和'\'
1 >>> re.search('[a|b]','aabcd').group() # search方法在字符集匹配时只匹配一次 2 'a' 3 >>> re.search('[a|b]+','aabcd'). # '+' 匹配一次或多次字符集中的内容 4 'aab' 5 6 >>> re.findall('[a|b]','aabcd') 7 ['a', 'a', 'b'] 8 >>>
'|' 匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC'
'(...)' 分组匹配,re.search("(abc){2}a(123|456)c", "abcabca456c").group() 结果 abcabca456c。 分组匹配时,findall只返回分组括号中的内容,返回到一个列表,分组作用:为了在已经匹配到的内容中再去提取过滤内容,有几个括号就提取几次
'\' 1.后面跟元字符去除特殊功能,2.跟普通字符实现特殊功能(\d:匹配数字),3.引用序号对应的字组所匹配的字符串
1 >>> re.search(r"(whisky)(\d+)com\2","whisky211985com211985").group() 2 'whisky211985com211985'
'\A' 效果和^是一样的,只从字符开头匹配,re.search("\Aabc","alexabc") 是匹配不到的
'\Z' 匹配字符结尾,同$
'\d' 匹配单个 数字0-9
'\D' 匹配非数字
'\w' 匹配[A-Za-z0-9],注意:不能匹配空格、特殊字符等
'\W' 匹配非[A-Za-z0-9]
'\s' 匹配空白字符、\t、\n、\r、\f、\v, re.search("\s+","ab\tc1\n3").group() 结果 '\t'
‘\b’ 匹配一个单词边界,即单词和空格(或特殊字符)间的位置,字符串开头结尾及空格回车等的位置,不会匹配空格符本身
1 >>> import re 2 >>> re.findall('abc\b','asdas abc ') 3 [] # 没有声明原生字符串或转义,所以结果为空 4 >>> re.findall('abc\\b','asdas abc ') 5 ['abc'] 6 >>> re.findall(r'abc\b','asdas abc ') 7 ['abc'] 8 >>> re.findall(r'abc\b','asdas abc*') 9 ['abc'] # 特殊字符作为单词边界 10 11 >>> re.findall(r'i\b','i miss iou') 12 ['i'] 13 >>> re.findall(r'i\b','imiss iou') 14 [] 15 >>> re.findall(r'\bi',' imiss iou') # 以空格为单词边界 16 ['i', 'i'] 17 >>> re.findall(r'\bi','imiss iou') #以字符串开头和空格为单词边界 18 ['i', 'i']
'(?P<name>...)' 分组匹配 re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})","371481199306143242").groupdict("city") 结果{'province': '3714', 'city': '81', 'birthday': '1993'}
注意:?P为固定语法格式
写法:(?P<组名>正则表达式),输出(以字典的形式输出):{'组名':'正则匹配的结果'}
(*|+|?|{})? : *、+、?、{}后面加?使用非贪婪模式
1 正则表达式中 .*? 代表什么? 2 解答: 3 点代表的是任意字符。* 代表的是取 0 至 无限长度问号代表的是非贪婪模式。三个链接在一起是取尽量少的任意字符,一般不会这么单独写。 4 5 用法: 6 他大多用在:.*?a 7 8 解释: 9 就是取前面任意长度的字符,到底一个 a 出现,匹配如下q@wer_qwerqweraljlkjlkjlkj, 10 11 得到:q@wer_qwerqwera 这部分,如果匹配不到后面的 a 字符,则匹配为空。
注意,re的若干方法:
match方法是从字符串开头往后匹配(用的少)
例:
res = re.match('^Chen', 'Chenronghua123') 语法:pattern,string
print(res)
#输出:<_sre.SRE_Match object; span=(0, 4), match='Chen'>
#res = re.match('r.+', 'Chen123ronghua123') #匹配结果为空,match从字符串开头开始匹配
# res = re.search('r.+', 'Chen123ronghua123') #search 从整个文本中搜索
# print(res.group())
# 结果:ronghua
#如果匹配不到返回None,即res为None,res.group()会报错
常用方法如下:
1.search是从整个文本中搜索,匹配到一个就返回
2.findall是从整个文本中搜索,贪婪匹配,如果匹配到多个全部返回,所有结果都放在一个列表中,findall没有group方法
1 >>> re.findall('[a-z]','wwwa.d') 2 ['w', 'w', 'w', 'a', 'd'] 3 >>> re.findall('[0-9]','w0w3w4a.99d') 4 ['0', '3', '4', '9', '9'] 5 6 >>> re.findall('\d','w0w3w4a.99d') 7 ['0', '3', '4', '9', '9'] 8 >>> re.findall('\d', 'aa22ww2qq123qwe333') 9 ['2', '2', '2', '1', '2', '3', '3', '3', '3'] 10 >>> re.findall('\d\d', 'aa22ww2qq123qwe333') 11 ['22', '12', '33'] 12 >>> re.findall('\d+', 'aa22ww2qq123qwe333') 13 ['22', '2', '123', '333'] 14 15 >>> re.findall('\w','w0w3w4a.99d') 16 ['w', '0', 'w', '3', 'w', '4', 'a', '9', '9', 'd'] 17 >>> re.findall('\s','w0w3 w4a.99d') 18 [' ']
findall常和分组()搭配使用,分组后利用findall方法只返回分组括号中的数据,爬虫常用,也可取消这种功能,例:
1 >>> re.findall('www.(?:baidu|123).com','asd www.baidu.com') # 在括号的组中加上'?:'即可匹配全部内容,而非仅是分组中的内容 2 ['www.baidu.com']
1 findall的分组、无分组: 2 >>> string = 'hello sky asd sky age sky qwe 19w' 3 >>> 4 >>> r = re.findall('a\w+', string) 5 >>> print(r) 6 ['asd', 'age'] 7 >>> r = re.findall('a(\w+)', string) #此时findall作用相当于groups,但groups把结果放入元组,findall则放入列表中 8 >>> print(r) 9 ['sd', 'ge'] 10 >>> 11 >>> string = 'hello sky asd sky age sky qwe 19w' 12 >>> r = re.findall('(a)(\w+)', string) 13 >>> print(r) 14 [('a', 'sd'), ('a', 'ge')] # string从前到后每次匹配到的字符串都放入元组,所有匹配到的内容都放入列表 15 >>> 16 >>> r = re.findall('(s)(\w+)(y)', string) # 相当于把groups中的内容放到一起 17 >>> print(r) 18 [('s', 'k', 'y'), ('s', 'k', 'y'), ('s', 'k', 'y')] #每个组中都有一个元素,共三个元素 19 >>> 20 >>> string = 'hello whisky asd whisky age whisky qwe 19w' 21 >>> r = re.findall('(i)(\w+(k))(y)', string) #第一个元素是i 第二个:sk 第三个k 第四个:y,有几个括号就提取几次,取出的元素一次放入元组中,最后再把每次匹配的结果放入列表 ,另外,这里不支持re.findall('(i)(\w+(k))(?P<n1>y)', string)模版组名,用finditer可以支持 22 >>> print(r) 23 [('i', 'sk', 'k', 'y'), ('i', 'sk', 'k', 'y'), ('i', 'sk', 'k', 'y')] 24 >>> 25 >>> string = 'hello whisky asd whisky age whisky qwe 19w' 26 >>> r = re.finditer('(i)(\w+(k))(?P<n1>y)', string) # 迭代方式可匹配组名 27 >>> for i in r: 28 ... print(i,i.group(),i.groups(),i.groupdict()) 29 ... 30 <_sre.SRE_Match object; span=(8, 12), match='isky'> isky ('i', 'sk', 'k', 'y') {'n1': 'y'} 31 <_sre.SRE_Match object; span=(19, 23), match='isky'> isky ('i', 'sk', 'k', 'y'){'n1': 'y'} 32 <_sre.SRE_Match object; span=(30, 34), match='isky'> isky ('i', 'sk', 'k', 'y'){'n1': 'y'} 33 >>> 34
1 findall:注意两个问题 2 1.findall无分组时匹配所有符合pattern的字符串 3 >>> n = re.findall('\d+\w\d+','a2b3c4d5') 4 >>> print(n) 5 ['2b3', '4d5'] # 结果不是3c4,可知findall方法是从前向后逐个匹配,匹配到结果2b3,则去掉2b3,再继续向后匹配 6 >>> 7 8 2.匹配到空的内容会返回 9 >>> n = re.findall('','a2b3c4d5') 10 >>> print(n) 11 ['', '', '', '', '', '', '', '', ''] 12 >>> 13 14 3.指定分组次数时,因为只有一个括号(分组),所以只匹配最后一次内容 15 >>> a = 'whisky' 16 >>> n = re.findall('(\w)(\w)(\w)(\w)', a) #表示分4组,全部返回 17 >>> print(n) 18 [('w', 'h', 'i', 's')] 19 >>> n = re.findall('(\w){4}', a) # 表示拿分组取字符串中匹配4次 20 >>> print(n) 21 ['s'] 22 >>> 23 24 4. findall中的正则(如 *)会匹配到空,那么结果就会输出空,在开发时要避免正则表达式内容为空: 25 >>> n = re.findall('(\dasd)*','1asd2asdp3asd98kif') 26 >>> print(n) 27 ['2asd', '', '3asd', '', '', '', '', '', ''] #*默认贪婪匹配,只有一个分组(括号),所以只返回2asd, *为空时,匹配结果为空,字符串最后默认有一个空字符,所以5个字符,匹配出6个空 28 >>> 29 >>> n = re.findall('(\dasd)+','1asd2asdp3asd4asd') #改为+,可以匹配想要的内容,不是输出空的结果 30 >>> print(n) 31 ['2asd', '4asd'] # 因为只有一个括号分组,所以只返回4asd,没有3asd
1 >>> import re 2 >>> p = re.compile(r'\d+') 3 >>> w = p.finditer('12 qwesdad44ers running, 22 ... 11 ...') 4 >>> for match in w: 5 ... match.group(),match.span() 6 ... 7 ('12', (0, 2)) 8 ('44', (10, 12)) 9 ('22', (25, 27)) 10 ('11', (32, 34))
小结:
findall其实就是一个一个的search,把search中的groups组合起来成为findall的内容,如果正则表达式中有一个分组,那么会去匹配组中的元素,放入列表;如果正则表达式中有多个组,会把组匹配到内容放到一个元组里面当作列表的一个元素,从前到后,重复此过程最后组成一个列表
3.match,group方法:
1 match 无分组: 2 >>> string = 'hello sky asd sky age sky qwe 19' 3 >>> r = re.match('h\w+',string) 4 >>> print(r.group()) # 获取匹配到的所有结果 5 hello 6 >>> print(r.groups()) # 只获取匹配到的分组结果 7 () 8 >>> print(r.groupdict()) # 获取匹配到的分组中所有执行了key的组 9 {} 10 >>> 11 12 match 有分组(分组作用:提取匹配成功的指定内容。先匹配成功全部正则,再把匹配成功的局部内容提取出来): 13 >>> string = 'hello sky asd sky age sky qwe 19' 14 >>> r = re.match('h(\w+)',string) 15 >>> print(r.group()) 16 hello 17 >>> print(r.groups()) 18 ('ello',) 19 >>> print(r.groupdict()) 20 {} 21 >>> 22 指定分组名,放入字典 23 >>> string = 'hello sky asd sky age sky qwe 19' 24 >>> r = re.match("(?P<n1>h)(?P<n2>\w+)",string) 25 >>> print(r.group()) 26 hello 27 >>> print(r.groups()) 28 ('h', 'ello') 29 >>> print(r.groupdict()) 30 {'n1': 'h', 'n2': 'ello'} 31 >>> 32 33 小结: 34 对于group方法,无论是否有分组,将会匹配正则表达式中匹配到的所有结果 35 对于groups方法,在有分组时,将匹配分组括号中(可以有多个分组)的内容,无分组则结果为空 36 对于groupdict方法,在有分组时,将匹配分组中所有执行了key的组。无分组则结果为空 37 写法:(?P<组名>正则表达式),输出(以字典的形式输出):{'组名':'正则匹配的结果'},常用语jangle中的路由系统 38 无分组匹配时,上述三个方法,只有group有意义
4.split分隔方法
1 # split 2 # 无分组 3 string = 'hello sky ald qwe lge zxc 27' 4 n = re.split('s\w+', string) 5 print(n) 6 # 输出 ['hello ', ' ald qwe lge zxc 27'] 7 8 # 有分组,split方法输出的结果如果用了分组,只保留分组中的内容 9 n = re.split('(s\w+)', string) 10 print(n) 11 # 输出 ['hello ', 'sky', ' ald qwe lge zxc 27'] 12 n = re.split('s(\w+)', string) 13 print(n) 14 # 输出 ['hello ', 'ky', ' ald qwe lge zxc 27'] 15 16 inpp = '1-2*((60-30 +(-40-5)*(9-2*5/3 + 7/3*99/4*2998 +10 * 568/14)) - (-4*3)/(16-3*2))' 17 n = re.split('\(([^()]+)\)', inpp) # 分割不含括号的最里层表达式,并去掉括号(表达式中转义符去匹配括号,里边括号作用split结果只取分组中的内容) 18 print(n) 19 # 输出 ['1-2*((60-30 +', '-40-5', '*', '9-2*5/3 + 7/3*99/4*2998 +10 * 568/14', ') - ', '-4*3', '/', '16-3*2', ')']
4.sub替换方法
1 # sub不涉及分组 2 string = '1qqas2uuii234ashjd888kkasd333' 3 n = re.sub('\d+', '----', string) 4 print(n) 5 new_str, count = re.subn('\d+', 'KKK', string) 6 print(new_str, count) 7 8 #输出: 9 ----qqas----uuii----ashjd----kkasd---- 10 ('KKKqqasKKKuuiiKKKashjdKKKkkasdKKK', 5)
5.start() 返回匹配开始的位置
end() 返回匹配结束的位置
6.span() 返回一个元组包含匹配(开始,结束)的位置
几个匹配模式:
1.re.I(re.IGNORECASE): 忽略大小写(括号内是完整写法,下同)
2.re.M(MULTILINE): 多行模式,改变'^'和'$'的行为(参见上图) [用得很少]
3.re.S(DOTALL): 使.匹配包括换行在内的所有字符,点任意匹配模式,改变'.'的行为
贪婪和非贪婪:
1 >>> re.search(r'a(\d+)','a3333333345b').group() 2 'a3333333345' 3 >>> re.search(r'a(\d+?)','a3333333345b').group() 4 'a3' 5 >>> re.search(r'a(\d*)','a3333333345b').group() 6 'a3333333345' 7 >>> re.search(r'a(\d*?)','a3333333345b').group() 8 'a' 9 10 如果分组括号两侧都有限制条件即:a()b,则分组中的?非贪婪匹配不再起作用 11 >>> re.findall(r'a(\d+)b','a23b') 12 ['23'] 13 >>> re.findall(r'a(\d+?)b','a23b') 14 ['23'] 15 >>>
原生字符串:(字符串前面加r,即声明使用原生字符串):
正则表达式中的r:(使用r前缀后,匹配字符串中所有字符都不转义,特殊字符不再有转义功能,只代表一个字符串)
r意思就是raw data,也就是原始数据,不用转义的。比如在一个字符串里面包含斜杠和一个字母n,"\n"就错了,这里的斜杠和n的组合在python中表示一个换行,必须"\\n",也就是用反斜杠来转义反斜杠。但是用r后面接字符串就没这个问题了,r"\n"中的\n就是这两个个字母本身了。
1 >>> import re 2 >>> re.search(r"\\","ba\cabc").group() 3 '\\' 4 >>> re.search("\\","ba\cabc").group() 5 Traceback (most recent call last): 6 File "<stdin>", line 1, in <module> 7 >>> re.search(r"\\","ba\cabc").group() 8 '\\' 9 >>> re.findall("\\","ba\cabc") 10 Traceback (most recent call last): 11 File "<stdin>", line 1, in <module> 12 >>> re.findall(r"\\","ba\cabc") 13 ['\\']
注:
在python中,\n是换行,\r是回车,\b是退格
1 python中关于正则内的\b,为什么使用\b时需要用r'\b',但是\w则不需要? 2 解答: 3 因为\b 有两种解释,而\w 只有一种。 4 5 \b的两种解释是: 6 7 1.'\b', 如果前面不加r, 那么解释器认为是转义字符“退格键backspace”; 8 2.r'\b', 如果前面加r, 那么解释器不会进行转义,\b 解释为正则表达式模式中的字符串边界。 9 10 而相比于\b, \w 只有第二种解释,并没有对应的转义字符,所以不加r, 也不会出错。
1 >>> import re 2 >>> re.findall('abc\b','asdas abc ') # 由于\b在python中表示退格,所以匹配不到内容 3 [] 4 >>> re.findall('abc\\b','asdas abc ') # 转义 5 ['abc'] 6 >>> re.findall(r'abc\b','asdas abc ') # 声明原生字符串,\b此时表示单词边界 7 ['abc'] 8 >>>
split方法:
res = re.split('[0-9]+', 'abc12de3f45GH')
print(res)
输出:['abc', 'de', 'f', 'GH']
sub方法:
res = re.sub('[0-9]+', '|', 'abc12de3f45GH', count=2)
print(res)
输出:abc|de|f45GH
1.re.I(re.IGNORECASE): 忽略大小写
res = re.search('[a-z]+', 'abcGH', flags=re.I)
print(res.group())
输出:abcGH
2.M(MULTILINE): 多行模式,改变'^'和'$'的行为
res = re.search(r"^a", "\nabc\neee", flags=re.M)
print(res.group())
输出:a
3.S(DOTALL): 点任意匹配模式,改变'.'的行为
res = re.search(".+", "\nabc\neee", flags=re.S)
print(res.group())
输出:a
举例:
'.' 默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行
res = re.match('.+', 'Chen123ronghua123')
print(res.group())
输出:
Chen123ronghua123
'$' 匹配字符结尾,或e.search("foo$","bfoo\nsdfsf",flags=re.MULTILINE).group()也可以
res = re.match('r.+', 'Chen123ronghua123') #匹配结果为空,match从字符串开头开始匹配
res = re.search('r.+', 'Chen123ronghua123') #search 从整个文本中搜索
print(res.group())
结果:ronghua
'+' 匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb']
res = re.search('r[a-z]+a', 'Chen123ronghua123') #匹配ronghua
print(res.group())
结果:ronghua
res = re.search('#.+#', '1123#hello#')
print(res.group())
结果:#hello#
'?' 匹配前一个字符1次或0次
res0 = re.search('aal?', 'aalex')
res1 = re.search('aal?', 'aaex')
print(res0.group())
print(res1.group())
输出
aal
aa
'{m}' 匹配前一个字符m次
res = re.search('[0-9]{3}', 'aa1xe2pp345lex') #匹配前面的数字三次
print(res.group())
'{n,m}' 匹配前一个字符n到m次
res = re.search('[0-9]{1,3}', 'aa1xe2pp345lex') #匹配前面的数字1到3次
print(res.group())
输出 1
findall 贪婪匹配
res = re.findall('[0-9]{1,3}', 'aa1xe2pp345lex') #findall,贪婪匹配,匹配前面的数字1到3次
print(res)
输出['1', '2', '345'] #以列表的形式返回
'|' 匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC'
res = re.search('abc|ABC', 'ABCBabcCD')
print(res.group())
输出 ABC
res = re.findall('abc|ABC', 'ABCBabcCD')
print(res)
输出 ['ABC', 'abc']
'(...)' 分组匹配,re.search("(abc){2}a(123|456)c", "abcabca456c").group() 结果 abcabca456c
res = re.search('(abc){2}', 'alexabcabc')
print(res.group())
输出 abcabc
res = re.search('(abc){2}(\|\|=){2}', 'alexabcabc||=||=') 匹配||= 两次,注意需要转义
print(res.group())
输出:abcabc||=||=
'\D' 匹配非数字
res = re.search('\D+', '123$- a')
print(res.group())
输出:$- a
'\w' 匹配[A-Za-z0-9] 除了特殊字符都匹配
res = re.search('\w+', '123$- a')
print(res.group())
输出:123
'\W' 匹配非[A-Za-z0-9] 只匹配特殊字符
res = re.search('\W+', '123$- ...a')
print(res.group())
输出:$- ...
'\s' 匹配空白字符、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 结果 '\t'
res = re.findall('\s', '123$- \r\n\t...a')
print(res)
输出:[' ', '\r', '\n', '\t']
>>> re.search('\s+', '123$- \r\n')
<_sre.SRE_Match object; span=(5, 9), match=' \t\r\n'>
'\A' 效果和^是一样的,只从字符开头匹配,re.search("\Aabc","alexabc") 是匹配不到的
'\Z' 匹配字符结尾,同$
'\d' 匹配数字0-9
例:
res = re.search('\A[0-9]+[a-z]\Z', '123a')
print(res.group())
输出:123a
* : 0个至多个
+ :1个至多个
res = re.match('^Chen\d+', 'Chen123ronghua123')
print(res)
print(res.group()) #查看匹配到的对象
输出:<_sre.SRE_Match object; span=(0, 7), match='Chen123'>
Chen123
'(?P<name>...)' 分组匹配
res = re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})","371481199306143242").groupdict("city")
print(res)
结果{'province': '3714', 'city': '81', 'birthday': '1993'}