Python正则表达式re模块
参考:https://www.cnblogs.com/cute/p/9186208.html
老男孩苑昊老师: http://www.cnblogs.com/yuanchenqi/articles/5732581.html
廖雪峰老师: https://www.liaoxuefeng.com/wiki/1016959663602400/1017639890281664
https://www.cnblogs.com/hanmk/p/9143514.html
爬虫部分的re模块使用:https://www.cnblogs.com/XJT2018/p/10312830.html
re模块(* * * * *)
就其本质而言,正则表达式(或 RE)是一种小型的、高度专业化的编程语言,(在Python中)它内嵌在Python中,并通过 re 模块实现。正则表达式模式被编译成一系列的字节码,然后由用 C 编写的匹配引擎执行。
正则 就是对字符串进行模糊匹配
字符匹配(普通字符,元字符):
1、普通字符:大多数字符和字母都会和自身匹配
>>> re.findall(\'alvin\',\'yuanaleSxalexwupeiqi\')
[\'alvin\']
2、元字符: . ^ $ * + ? { } [ ] | ( ) \ 的详细解释:
^
匹配行首,以 xxx为开头
$
匹配行尾,以xxx结尾。
\
转义字符,如果要匹配\本身,需要使用再次转义:\\。
一些特殊字符:
\d:匹配:[0-9] \D:匹配:[^0-9] \s:匹配:任何空白符,即:[\t\n\r\f\v] \S:匹配:任何非空白符,即:[^\t\n\r\f\v] \w:匹配:[a-zA-Z0-9_ ] \W:匹配:[^a-zA-Z0-9_ ]
*
匹配前一个字符或子表达式出现 0次或多次。
+
匹配前一个字符或子表达式出现 1次或多次。
?
(1) 匹配前一个字符或子表达式出现 0次或1次。
(2) 跟在 * 或 + 后面表示非贪婪匹配:
*? 匹配0个
+? 匹配1个
import re # 贪婪模式,会尽量多地去匹配 r1 = re.compile(r\'ab+\') s1 = \'abbb\' print(re.findall(r1,s1)) #[\'abbb\'] # 非贪婪模式,会尽量少地去匹配 r2 = re.compile(r\'ab+?\') s2 = \'abbb\' print(re.findall(r2,s2)) #[\'ab\']
{}
匹配前一个字符或子表达式出现指定次数:
-
{0,}:0次或多次,相当于* -
{1,}:1次或多次,相当于+ -
{0,1}:0次或1次,相当于? -
{m,n}:m次到n次(m <= n)
import re ret=re.findall(\'a..in\',\'helloalvin\') print(ret)#[\'alvin\'] ret=re.findall(\'^a...n\',\'alvinhelloawwwn\') print(ret)#[\'alvin\'] ret=re.findall(\'a...n$\',\'alvinhelloawwwn\') print(ret)#[\'awwwn\'] ret=re.findall(\'a...n$\',\'alvinhelloawwwn\') print(ret)#[\'awwwn\'] ret=re.findall(\'abc*\',\'abcccc\')#贪婪匹配[0,+oo] print(ret)#[\'abcccc\'] ret=re.findall(\'abc+\',\'abccc\')#[1,+oo] print(ret)#[\'abccc\'] ret=re.findall(\'abc?\',\'abccc\')#[0,1] print(ret)#[\'abc\'] ret=re.findall(\'abc{1,4}\',\'abccc\') print(ret)#[\'abccc\'] 贪婪匹配
注意:前面的 *,+,?等都是贪婪匹配,也就是尽可能匹配,后面加?号使其变成惰性匹配(非贪婪匹配)
ret=re.findall(\'abc*?\',\'abcccccc\') print(ret)#[\'ab\']
跟着教程敲代码练习:
""" 第一类元字符 . ^ $ * + ? {}""" import re """ . 匹配除了 \n 之外的任意字符,一个 . 只能匹配一个字符 """ re.findall("alex","djkalfjioealexieou") ##[\'alex\'] re.findall("a..x",\'jowutojoauixuoero\') #[\'auix\'] re.findall("a...x",\'jowutojoauixuoero\') #[] """ ^ 开头匹配 """ re.findall("^a...x",\'jowutojoauixuoero\') #[] re.findall("^j..u",\'jowutojowuixuoero\') #[\'jowu\'] re.findall("j..u",\'jowutojowuixuoero\') #[\'jowu\', \'jowu\'] """ $ 结尾匹配 """ re.findall("j..u$",\'jowutojowuixuoejrou\') #[\'jrou\'] re.findall("j..u$",\'jowutojowuixuoejrou$\') #[] """ * 紧挨着的字符 匹配0~无穷次 """ """ + 紧挨着的字符 匹配1~无穷次 """ re.findall("alex*","uoghkalexxx") #[\'alexxx\'] re.findall("alex+","uoghkalexxx") #[\'alexxx\'] re.findall("alex*","uoghkale") #[\'ale\'] re.findall("alex+","uoghkale") #[] """ ? 紧挨着的字符 匹配0~1 """ re.findall("alex?","uoghkale") #[\'ale\'] re.findall("alex?","uoghkalex") #[\'alex\'] re.findall("alex?","uoghkalexxxx") #[\'alex\'] """ {} 是上面 * + ? 的集合,并且可自定义 {0,} <===> * {1,} <===> + {0,1} <===> ? {1,6} 1 2 3 4 5 6 都可以 """ re.findall("alex{6}","uoghkalexxxxxx") #[\'alexxxxxx\'] re.findall("alex{0,6}","uoghkalexxx") #[\'alexxx\'] re.findall("alex{0,6}","uoghkale") #[\'ale\'] re.findall("alex{0,1}","uoghkale") #[\'ale\'] re.findall("alex{0,1}","uoghkalex") #[\'alex\'] # 惰性匹配 在元字符后面加上 ? re.findall("alex*?","uoghkalexxxx") #[\'ale\'] re.findall("alex+?","uoghkalexxxx") #[\'alex\'] re.findall("alex+","uoghkalexxxx") #[\'alexxxx\']
元字符之字符集[]:
(1) 常用来指定一个字符集,如[abc]匹配:a或b或c
(2) 元字符. * ? + ^ $ 在[]中不起所用,比如:[a+]匹配:a或+
但注意:在方括号中:要匹配转义符“\”本身,要用:\\;要匹配方括号开头的^符本身,要用:\^;要匹配-字符,需要用:\-
(3) 补集匹配:[^a],匹配非a的一个字符,^在[ ]中表示取非
(4) 匹配连续字符:[a-zA-Z0-9],匹配大小写英文字母和数字
#--------------------------------------------字符集[] ret=re.findall(\'a[bc]d\',\'acd\') print(ret)#[\'acd\'] ret=re.findall(\'[a-z]\',\'acd\') print(ret)#[\'a\', \'c\', \'d\'] ret=re.findall(\'[.*+]\',\'a.cd+\') print(ret)#[\'.\', \'+\'] #在字符集里有功能的符号: - ^ \ ret=re.findall(\'[1-9]\',\'45dha3\') print(ret)#[\'4\', \'5\', \'3\'] ret=re.findall(\'[^ab]\',\'45bdha3\') print(ret)#[\'4\', \'5\', \'d\', \'h\', \'3\'] ret=re.findall(\'[\d]\',\'45bdha3\') print(ret)#[\'4\', \'5\', \'3\']
跟着教程敲代码练习:
""" [] 中:^ 非 ,- 表示从。。到。。 a-z 所有小写字母,\ 转义 \( 就表示左括号。 其他的元字符失去意义,如 * 不表示(0-无穷),()就只是表示括号 """ ret = re.findall(\'a[bc]d\', \'acd\') # 匹配 abd 或 acd print(ret) # [\'acd\'] ret = re.findall(\'[a-z]\', \'acd\') #一个字符 从a到z print(ret) # [\'a\', \'c\', \'d\'] ret = re.findall(\'[.*+]\', \'a.cd+\') #* 失去特殊意义 就表示一个 * print(ret) # [\'.\', \'+\'] # 在字符集里有功能的符号: - ^ \ ret = re.findall(\'[1-9]\', \'45dha3\') print(ret) # [\'4\', \'5\', \'3\'] ret = re.findall(\'[^ab]\', \'45bdha3\') print(ret) # [\'4\', \'5\', \'d\', \'h\', \'3\'] ret = re.findall(\'[\d]\', \'45bdha3\') print(ret) # [\'4\', \'5\', \'3\']
# 一个特殊案例:匹配表达式里面 最内层括号(13-6) re.findall("\([^()]*\)","12+(34*6+2-5*(13-6))") #[\'(13-6)\']
元字符之转义符 \
反斜杠后边跟元字符去除特殊功能,比如 \.
反斜杠后边跟普通字符实现特殊功能,比如 \d
- \d 匹配任何十进制数;它相当于类 [0-9]。
- \D 匹配任何非数字字符;它相当于类 [^0-9]。
- \s 匹配任何空白字符;它相当于类 [ \t\n\r\f\v]。
- \S 匹配任何非空白字符;它相当于类 [^ \t\n\r\f\v]。
- \w 匹配任何字母数字字符;它相当于类 [a-zA-Z0-9_]。
- \W 匹配任何非字母数字字符;它相当于类 [^a-zA-Z0-9_]
- \b 匹配一个特殊字符边界,比如空格 ,&,#等
ret=re.findall(\'I\b\',\'I am LIST\') print(ret)#[] ret=re.findall(r\'I\b\',\'I am LIST\') print(ret)#[\'I\'] r\'I\b\' 其中 r=raw 表示原始的 未经处理的 re.findall(r\'I\b\',\'I am LIST\') #r\'I\b\' 要先经过Python解释器编译 然后传递给re模块,re模块处理 \b 匹配到 空格
现在我们聊一聊 \ ,先看下面两个匹配:
#-----------------------------eg1: import re ret=re.findall(\'c\l\',\'abc\le\') print(ret)#[] ret=re.findall(\'c\\l\',\'abc\le\') print(ret)#[] ret=re.findall(\'c\\\\l\',\'abc\le\') print(ret)#[\'c\\l\'] ret=re.findall(r\'c\\l\',\'abc\le\') print(ret)#[\'c\\l\'] #-----------------------------eg2: #之所以选择\b是因为\b在ASCII表中是有意义的 m = re.findall(\'\bblow\', \'blow\') print(m) m = re.findall(r\'\bblow\', \'blow\') print(m)
由于Python的字符串本身也用 \转义,所以要特别注意:
s = \'ABC\\-001\' # Python的字符串 # 对应的正则表达式字符串变成: # \'ABC\-001\'
因此我们强烈建议使用Python的 r前缀,就不用考虑转义的问题了:
s = r\'ABC\-001\' # Python的字符串 # 对应的正则表达式字符串不变: # \'ABC\-001\'
元字符之分组 ()
(?P<分组名>\d+) 有名分组 可以通过ret.group(\'分组名\') 取得匹配结果
m = re.findall(r\'(ad)+\', \'add\') print(m) #ad ret=re.search(\'(?P<id>\d{2})/(?P<name>\w{3})\',\'23/com\') print(ret.group()) #23/com print(ret.group(\'id\')) #23 ##\'(?P<id>\d{2})/(?P<name>\w{3})\' ?P<id> 分组 组名为ip, \d{2} 匹配后面两位整数 #(?P<name>\w{3}) ?P<id> 分组 组名为name,\w{3} 匹配3位 大小写字母 数字 下划线
>>> re.search(r\'<a.*>(.*)</a>.*<p>(.*)</p>\',s).group(1) \'更多\' >>> re.search(r\'<a.*>(.*)</a>.*<p>(.*)</p>\',s).group(2) \'dfsl\' >>> ss = re.search(r\'<a.*>(?P<txt>.*)</a>.*<p>(?P<txt2>.*)</p>\',s) >>> ss.group(\'txt\') \'更多\' >>> ss.group(\'txt2\')
练习:
>>> re.findall(r\'(abc)+\',\'adklnvkaj45d6kasdfeabcwoibcfabcabcabcdf\') [\'abc\', \'abc\'] >>> re.findall(r\'abc+\',\'adklnvkaj45d6kasdfeabcwoibcfabcabcabcdf\') [\'abc\', \'abc\', \'abc\', \'abc\'] >>> re.findall(r\'(abc)+\',\'abcabcabcabc\') [\'abc\'] >>> re.findall(r\'(abc)*\',\'abcabcabcabc\') [\'abc\', \'\'] >>> re.findall(r\'(ab)+\',\'abcabcabcabc\') [\'ab\', \'ab\', \'ab\', \'ab\'] >>> re.findall(r\'(abc)+\',\'abcabcabcabc\') [\'abc\'] >>> re.findall(r\'(abc)+\',\'abcccccc\') [\'abc\'] >>> re.findall(r\'(?P<name>[a-z]+)\d+\',\'xiong26wang33zhang23sun56\') [\'xiong\', \'wang\', \'zhang\', \'sun\'] >>> re.search(r\'(?P<name>[a-z]+)\d+\',\'xiong26wang33zhang23sun56\') <_sre.SRE_Match object; span=(0, 7), match=\'xiong26\'> >>> re.search(r\'(?P<name>[a-z]+)\d+\',\'xiong26wang33zhang23sun56\').group() \'xiong26\' >>> re.search(r\'(?P<name>[a-z]+)\d+\',\'xiong26wang33zhang23sun56\').group("name") \'xiong\' >>> re.search(r\'(?P<name>[a-z]+)(?P<age>\d+)\',\'xiong26wang33zhang23sun56\').group("name") \'xiong\' >>> re.search(r\'(?P<name>[a-z]+)(?P<age>\d+)\',\'xiong26wang33zhang23sun56\').group("age") \'26\'
元字符之|
ret=re.search(\'(ab)|\d\',\'rabhdg8sd\') print(ret.group())#ab
re模块下的常用方法
# 1 re.findall(\'a\', \'alvin yuan\') # 返回所有满足匹配条件的结果,放在列表里 # 2 re.search(\'a\', \'alvin yuan\').group() # 函数会在字符串内查找模式匹配,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以 # 通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。 # 3 re.match(\'a\', \'abc\').group() # 同search,不过要在字符串开始处进行匹配,相当于re.search(\'^\',\'\') # 4 ret = re.split(\'[ab]\', \'abcd\') # 先按\'a\'分割得到\'\'和\'bcd\',在对\'\'和\'bcd\'分别按\'b\'分割 print(ret) # [\'\', \'\', \'cd\'] # 5 ret = re.sub(\'\d\', \'A\', \'alvin5yuan6\', 1) ## 替换 数字 替换为 A , 只替换第一个 print(ret) # alvinAyuan6 ret = re.subn(\'\d\', \'A\', \'alvin5yuan6\') #将替换结果 和 替换个数 返回为一个元组 print(ret) # (\'alvinAyuanA\', 2) # 6 obj = re.compile(\'\d{3}\') ## 编译,如果使用多次匹配规则 效率就体现出来了 ret = obj.search(\'abc123eeee\') print(ret.group()) # 123
处理大量数据,匹配结果返回一个迭代器
import re ret=re.finditer(\'\d\',\'ds3sy4784a\') print(ret) #<callable_iterator object at 0x10195f940> print(next(ret).group()) print(next(ret).group())
注意:
ret = re.findall(\'www.(baidu|oldboy).com\', \'www.oldboy.com\') print(ret) # [\'oldboy\'] 这是因为findall会优先把匹配结果分组里内容返回,如果想要匹配结果,取消权限即可 ret = re.findall(\'www.(?:baidu|oldboy).com\', \'www.oldboy.com\') print(ret) # [\'www.oldboy.com\']
补充练习题:
>>> import re >>> re.findall(\'\d+\',\'nka15640-8dasgwe-96dagv-6\') [\'15640\', \'8\', \'96\', \'6\'] >>> re.findall(\'^\d+\',\'nka15640-8dasgwe-96dagv-6\') [] >>> re.findall(\'[^-]\d+\',\'nka15640-8dasgwe-96dagv-6\') [\'a15640\', \'96\'] >>> re.findall(\'\d{3}\s+\d{3,8}\',\'sdfag123 7845agewgv\') [\'123 7845\'] >>> re.findall(\'\d+@$\',\'1351655382@qq.com\') [] >>> re.findall(\'\d+\',\'1351655382@qq.com\') [\'1351655382\'] >>> re.findall(r\'ka|b\',\'adklnvkj45d6kaewoib\') [\'ka\', \'b\'] >>> re.findall(r\'ka|b\',\'adklnvkj45d6ksdfewoibfadf\') [\'b\'] >>> re.findall(r\'ka|bc\',\'adklnvkaj45d6kasdfewoibcfadf\') [\'ka\', \'ka\', \'bc\'] >>> re.findall(r\'(abc)+\',\'adklnvkaj45d6kasdfeabcwoibcfabcabcabcdf\') [\'abc\', \'abc\'] >>> re.findall(r\'abc+\',\'adklnvkaj45d6kasdfeabcwoibcfabcabcabcdf\') [\'abc\', \'abc\', \'abc\', \'abc\'] >>> re.findall(r\'(abc)+\',\'abcabcabcabc\') [\'abc\'] >>> re.findall(r\'(abc)*\',\'abcabcabcabc\') [\'abc\', \'\'] >>> re.findall(r\'(ab)+\',\'abcabcabcabc\') [\'ab\', \'ab\', \'ab\', \'ab\'] >>> re.findall(r\'(abc)+\',\'abcabcabcabc\') [\'abc\'] >>> re.findall(r\'(abc)+\',\'abcccccc\') [\'abc\'] >>> re.findall(r\'(?P<name>[a-z]+)\d+\',\'xiong26wang33zhang23sun56\') [\'xiong\', \'wang\', \'zhang\', \'sun\'] >>> re.search(r\'(?P<name>[a-z]+)\d+\',\'xiong26wang33zhang23sun56\') <_sre.SRE_Match object; span=(0, 7), match=\'xiong26\'> >>> re.search(r\'(?P<name>[a-z]+)\d+\',\'xiong26wang33zhang23sun56\').group() \'xiong26\' >>> re.search(r\'(?P<name>[a-z]+)\d+\',\'xiong26wang33zhang23sun56\').group("name") \'xiong\' >>> re.search(r\'(?P<name>[a-z]+)(?P<age>\d+)\',\'xiong26wang33zhang23sun56\').group("name") \'xiong\' >>> re.search(r\'(?P<name>[a-z]+)(?P<age>\d+)\',\'xiong26wang33zhang23sun56\').group("age") \'26\' >>> re.findall(\'(ab)|\d\',\'rabhdg8sd\') [\'ab\', \'\'] >>> re.findall(\'(ab)|\d+\',\'rabhdg8sd\') [\'ab\', \'\'] >>> re.findall(\'(ab)|\d+\',\'rab632hdg8sd\') [\'ab\', \'\', \'\'] >>> re.findall(\'(ab)|\d\',\'rab632hdg8sd\') [\'ab\', \'\', \'\', \'\', \'\'] >>> re.findall(\'(ab)|\d+\',\'rab632hdg8sd\') [\'ab\', \'\', \'\'] >>> re.findall(\'(ab).*\d+\',\'rab632hdg8sd\') [\'ab\'] >>> re.match(\'a\', \'abc\').group() \'a\' >>> re.match(\'a\', \'abcadfa\').group() \'a\' >>> ####################split分割 >>> re.split(\' \',\'hello world xiong\') [\'hello\', \'world\', \'xiong\'] >>> re.split(\'[ |]\',\'hello world|xiong\') [\'hello\', \'world\', \'xiong\'] >>> re.split(\'[ab]\',\'vnklaouojbouhg\') [\'vnkl\', \'ouoj\', \'ouhg\'] >>> re.split(\'[ab]\',\'auouabuobuo\') [\'\', \'uou\', \'\', \'uo\', \'uo\'] >>> re.split(\'[ab]\',\'asdabcd\') [\'\', \'sd\', \'\', \'cd\'] >>> re.split(\'[ab]\',\'abc\') [\'\', \'\', \'c\'] >>> >>>############# \'替换\' >>> re.sub(\'\d+\',\'A\',\'as451da23bc23d5dae\') \'asAdaAbcAdAdae\' >>> re.sub(\'\d\',\'A\',\'as451da23bc23d5dae\') \'asAAAdaAAbcAAdAdae\' >>> re.sub(\'\d\',\'A\',\'as451da23bc23d5dae\',3) \'asAAAda23bc23d5dae\' >>> re.sub(\'\d\',\'A\',\'as451da23bc23d5dae\',2) \'asAA1da23bc23d5dae\' >>> re.subn(\'\d\',\'A\',\'as451da23bc23d5dae\') (\'asAAAdaAAbcAAdAdae\', 8) >>> ###########compile编译 >>> com = re.compile("\d+") >>> com.findall("uoihja37jdaogj230ldaoje") [\'37\', \'230\'] >>> >>> re.findall("\d","af7nka89wejojk03oajag") [\'7\', \'8\', \'9\', \'0\', \'3\'] >>> re.finditer("\d","af7nka89wejojk03oajag") <callable_iterator object at 0x000001E1D2836BE0> >>> ret = re.finditer("\d","af7nka89wejojk03oajag") >>> ret.__next__() <_sre.SRE_Match object; span=(2, 3), match=\'7\'> >>> ret.__next__().group() \'8\' >>>#############结果返回为一个迭代器 >>> ret = re.finditer("www\.(baidu|163)\.com","af7nkawww.baidu.com89wejojk03oajag") >>> re.finditer("www\.(baidu|163)\.com","af7nkawww.baidu.com89wejojk03oajag") <callable_iterator object at 0x000001E1D2836B00> ###################匹配结果优先返回为匹配到的分组内容 >>> re.findall("www\.(baidu|163)\.com","af7nkawww.baidu.com89wejojk03oajag") [\'baidu\'] >>> re.findall("www\.(?:baidu|163)\.com","af7nkawww.baidu.com89wejojk03oajag") [\'www.baidu.com\']
补充1:
import re print(re.findall("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")) print(re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")) print(re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>"))
补充2:
#匹配出所有的整数 import re #ret=re.findall(r"\d+{0}]","1-2*(60+(-40.35/5)-(-4*3))") ret=re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))") ret.remove("") print(ret)