正则的用法
正则表达式: 匹配内容,findall()方法将匹配到的内容返回列表,需引入re模块
1.1 匹配单个字符:
import re # 首先引入re模块 # 匹配单个字符的用法: # (1) \d:匹配数字 lst = re.findall("\d","字符 1q2we34re6fdsf8dg") print(lst) # ['1', '2', '3', '4', '6', '8'] 将后面的字符串按照元素拆开,是数字的就放到列表里面 # (2) \D: 匹配非数字 lst = re.findall("\D","23fjs字符") print(lst) # ['f', 'j', 's', '字', '符'] # (3) \w: 匹配数字字母下滑线和中文 lst = re.findall("\w","27 j3 _d_晴朗") print(lst) # ['2', '7', 'j', '3', '_', 'd', '_', '晴', '朗'] # (4) \W: 匹配非数字字母下划线中文 lst = re.findall("\W"," 3fs字符 %#") print(lst) # [' ', ' ', '%', '#'] # (5) \s: 匹配任意空格符 \n,\r,\t lst = re.findall("\s"," 38s sjf \t ") print(lst) # [' ', ' ', ' ', '\t', ' ', ' '] # (6) \S: 匹配非空格符 lst = re.findall("\S","34sdf 9 \t ") print(lst) # ['3', '4', 's', 'd', 'f', '9'] # (7) \n: 匹配换行符 str1 = """ 换行显示 """ lst = re.findall("\n",str1) print(lst) # ['\n', '\n', '\n']
1.2 字符组: 匹配中括号里列举的内容
lst = re.findall("[abc]","oputuopctyauobpt") print(lst) # ['c', 'a', 'b'] print(re.findall('a[abc]b','aab abb acb adb')) # ['aab', 'abb', 'acb'] # 匹配的数据必须以a开头,以b结尾, print(re.findall('a[0123456789]b','a1b a2b a3b acb ayb')) # ['a1b', 'a2b', 'a3b'] # 优化: -是一个特殊的字符,代表的是一个范围 0-9 0123456789 print(re.findall('a[0-9]b','a1b a2b a3b acb ayb')) # ['a1b', 'a2b', 'a3b'] print(re.findall('a[abcdefg]b','a1b a2b a3b acb ayb adb')) # ['acb', 'adb'] # 优化: [a-g] 如果想要表达所有的26个小写字母[a-z] print(re.findall('a[a-g]b','a1b a2b a3b acb ayb adb')) # ['acb', 'adb'] print(re.findall('a[ABCDEFG]b','a1b a2b a3b aAb aDb aYb')) # ['aAb', 'aDb'] # 优化: [A-G] 如果想要表达所有的26个大写字母[A-Z] print(re.findall('a[A-G]b','a1b a2b a3b aAb aDb aYb')) # ['aAb', 'aDb'] print(re.findall('a[0-9a-zA-Z]b','a-b aab aAb aWb aqba1b')) # ['aab', 'aAb', 'aWb', 'aqb', 'a1b'] # 注意: [0-z] 数字 小写 大写 还有特殊字符 比如@ ... print(re.findall('a[0-z]b','a@b aab aAb aWb aqba1b')) # ['a@b', 'aab', 'aAb', 'aWb', 'aqb', 'a1b'] print(re.findall('a[0-9][*#/]b','a1/b a2b a29b a56b a456b')) # ['a1/b']
1.3 量词: 多个字符的匹配 ?,+,*
# ?: 代表匹配?前面字符的0次或者1次 print(re.findall('a?b','abbzab abb aab')) # ['ab', 'b', 'ab', 'ab', 'b', 'ab'] # +: 代表匹配+前面字符的1次或者多次,就是至少一次 print(re.findall('a+b','b ab aaaaaab abb')) # ['ab', 'aaaaaab', 'ab'] # *:代表匹配*前面字符的0次或者多次,就是任意次 print(re.findall('a*b','b ab aaaaaab abbbbbbb')) # ['b', 'ab', 'aaaaaab', 'ab', 'b', 'b', 'b', 'b', 'b', 'b'] # {m,n}: 匹配m到n次,包含m和n # 1 <= a <= 3 print(re.findall('a{1,3}b','aaab ab aab abbb aaz aabb')) # ['aaab', 'ab', 'aab', 'ab', 'aab'] # 前面修饰的a , 必须是2个字符 print(re.findall('a{2}b','aaab ab aab abbb aaz aabb')) # ['aab', 'aab', 'aab'] # 前面修饰的a , 至少是2个字符 print(re.findall('a{2,}b','aaab ab aab abbb aaz aabb')) # ['aaab', 'aab', 'aab']
1.4 贪婪模式与非贪婪模式: 一般默认是贪婪模式, 用?在量词后面修饰变成非贪婪模式
贪婪模式: 默认向更多次匹配,底层用回溯算法,走到最后再回头,返回最后一个符合条件的匹配值, 效率比非贪婪模式高
非贪婪模式:默认匹配更少的次数,用一个?修饰在量词的后面
strvar = "刘能和刘老根和刘铁棍子777子888" # 贪婪匹配 lst = re.findall("刘.",strvar) print(lst) # ['刘能', '刘老', '刘铁'] lst = re.findall("刘.?",strvar) print(lst) # ['刘能', '刘老', '刘铁'] lst = re.findall("刘.+",strvar) print(lst) # ['刘能和刘老根和刘铁棍子777子888'] lst = re.findall("刘.*",strvar) print(lst) # ['刘能和刘老根和刘铁棍子777子888'] lst = re.findall("刘.{1,21}",strvar) print(lst) # ['刘能和刘老根和刘铁棍子777子888'] lst = re.findall("刘.*子",strvar) print(lst) # ['刘能和刘老根和刘铁棍子777子'] # 非贪婪匹配 lst = re.findall("刘.??",strvar) print(lst) # ['刘', '刘', '刘'] lst = re.findall("刘.+?",strvar) print(lst) # ['刘能', '刘老', '刘铁'] lst = re.findall("刘.*?",strvar) print(lst) # ['刘', '刘', '刘'] lst = re.findall("刘.{1,21}?",strvar) print(lst) # ['刘能', '刘老', '刘铁'] lst = re.findall("刘.*?子",strvar) print(lst) # ['刘能和刘老根和刘铁棍子'] lst = re.findall("天??","天空很蓝天气很好") print(lst) # ['', '', '', '', '', '', '', '', ''] # 代表0次或者1次的非贪婪匹配,所以匹配为空
1.5 特殊字符: ^, -, \, ., \b, ^, $
# ^ 在字符组当中, 代表除了中括号里的元素,需放到字符组的左边第一个位置 print(re.findall('a[^-+*/]b',"a%b ccaa*bda&bd")) # ['a%b', 'a&b'] 除开-+*/这些字符,匹配其余符合条件的 # 如果想要匹配^ 或者 -, 在原来的字符前面加上\,让字符的含义失效. lst = re.findall(r"e[\^\-]f","e^f e-f") print(lst) # ['e^f', 'e-f'] # 匹配 \,在斜杠前再加一个斜杠,防止斜杠转义,代表一个斜杠 lst = re.findall(r"a\\c","a\c") print(lst) # ['a\\c'] 两个斜杠实际代表一个 print(lst[0]) # a\c # .表示匹配任意字符,除了换行符\n lst = re.findall(".","w3sf\n") print(lst) # ['w', '3', 's', 'f'] lst = re.findall(".","33s . s\n") print(lst) # ['3', '3', 's', ' ', '.', ' ', 's'] # 边界符 \b ^ $ # \b表示卡住字符的开头和结尾,是一个转义字符 strvar = "word pwd scf" lst = re.findall(r".*d\b",strvar) # 表示元素必须以d结尾 print(lst) # ['word pwd'] lst = re.findall(r".*?d\b",strvar) print(lst) # ['word', ' pwd'] lst = re.findall(r"\bw",strvar) print(lst) # ['w'] 只匹配w且以w开头 lst = re.findall(r"\bw.*?",strvar) print(lst) # ['w'] # # 正则表达式中写字符时,要谨慎,下面例子必须匹配到第一个空格时,才结束 lst = re.findall(r"\bw.*? ",strvar) print(lst) # ['word '] lst = re.findall(r"\bw\S*",strvar) print(lst) # ['word'] # ^ $: ^表示必须以..开头,$表示必须以..结尾.如果字符串中出现^和$,则要把这个字符串当做一个整体来匹配 strvar = "天空天气天气" print(re.findall('天.',strvar)) # ['天空', '天气', '天气'] print(re.findall('^天.',strvar)) # ['天空'] print(re.findall('天.$',strvar)) # ['天气'] print(re.findall('^天.$',strvar)) # [] 将字符串当做整体来看,没有以.结尾的 print(re.findall('^天.*?$',strvar)) # ['天空天气天气'] 将字符串当做整体来看 print(re.findall('^天.*?天$',strvar)) # [] print(re.findall('^天.*?气$',strvar)) # ['天空天气天气'] print(re.findall('^g.*? ' , 'giveme 1gfive gay')) # ['giveme '] print(re.findall('five$' , 'aassfive')) # ['five'] print(re.findall('^giveme$' , 'giveme')) # ['giveme'] print(re.findall('^giveme$' , 'giveme giveme')) # [] print(re.findall('giveme' , 'giveme giveme')) # ['giveme', 'giveme'] print(re.findall("^g.*e",'giveme 1gfive gay')) # ['giveme 1gfive']
1.6 匹配分组: 用(),有分组的功能,可以优先显示,用?:表示取消括号里的优先分组
# 要所有的姓名 str1 = 'wusir_good alex_good secret男_good' print(re.findall("(.*?)_good",str1)) # ['wusir', ' alex', ' secret男'] 括号里优先显示 # ?: 不显示括号里面的内容 print(re.findall('(?:.*?)_good','wusir_good alex_good secret男_good')) # ['wusir_good', ' alex_good', ' secret男_good'] # |表示或,匹配到|前面或者后面的字符都符合条件,通常都将较难(较长)的条件放|前面,将较易(较短)的条件放|后面 strvar = "abcddd" lst = re.findall("a|b",strvar) print(lst) # ['a', 'b'] strvar = "abc24234234ddabcd234234" lst = re.findall("abc|abcd",strvar) print(lst) # ['abc', 'abc'] 优先匹配abc,在匹配abcd,所以只匹配到abc lst = re.findall("abcd|abc",strvar) print(lst) # ['abc', 'abcd'] 优先匹配abcd,在匹配abc # (1)匹配小数 strvar = "5.33 3.13 34 34. .98 9.99 sdfsdf ......" lst = re.findall(r"\d+\.\d+",strvar) print(lst) # ['5.33', '3.13', '9.99'] # (2)匹配小数和整数 strvar = "5.33 3.13 34 34. .98 9.99 35.5.5sdfsdf ......" lst = re.findall(r"\d+(?:\.?\d*)",strvar) print(lst) # ['5.33', '3.13', '34', '34.', '98', '9.99', '35.5', '5'] # 匹配135或171的手机号 strvar = "asdfasd234 13591199444 17188886666 19145547744" lst = re.findall("(?:135|171)\d{8}",strvar) print(lst) # ['13591199444', '17188886666'] # 卡主开头和结尾,数字必须是11位 strvar = "13591199444" lst = re.findall("^1\d{9}\d$",strvar) print(lst) # ['13591199444']
2. search函数: 只要找到一个匹配的值就返回,返回的是一个对象,可以把分组内容和匹配内容全部显示出来
用对象.group()获取匹配的内容, 用对象.groups获取分组的内容
# 匹配www.baidu.com 或者 www.oldboy.com strvar = "www.baidu.com" lst = re.findall("^www\.(?:baidu|oldboy)\.com$",strvar) print(lst) # ['www.baidu.com'] obj = re.search("(www)\.(baidu|oldboy)\.(com)",strvar) print(obj) # <_sre.SRE_Match object; span=(0, 13), match='www.baidu.com'> print(obj.group()) # www.baidu.com print(obj.groups()) # ('www', 'baidu', 'com') # 获取第一个小括号里面的内容 print(obj.group(1)) # www # # 获取第二个小括号里面的内容 print(obj.group(2)) # baidu # # 获取第三个小括号里面的内容 print(obj.group(3)) # com # # ### 案例:用正则做运算 "5*6-7/3" 匹配 5*6 或者 7/3 # """search 在匹配不到时,返回的是None,无法使用group""" # # 匹配出5*6 strvar = "5*6-7/3" # # 匹配出5*6 obj = re.search(r"\d+[*/]\d+",strvar) print(obj) strvar1 = obj.group() print(strvar1) # 5*6 # 按照*号分隔,算出乘积结果 n1,n2 = strvar1.split("*") res1 = int(n1)*int(n2) print(res1) # 对字符串进行替换 # strvar = "30-7/3" strvar2 = strvar.replace(strvar1,str(res1)) print(strvar2) # 30-7/3 # 匹配出 7/3 obj = re.search(r"\d+[*/]\d+",strvar2) strvar3 = obj.group() print(strvar3) # 7/3 # 按照/号分隔,算出除法结果 n1,n2 = strvar3.split("/") res2 = int(n1)/int(n2) print(res2) # 对字符串进行替换 strvar4 = strvar2.replace(strvar3,str(res2)) print(strvar4) # 通过-号进行最后的分隔,算出最后的结果. n1,n2 = strvar4.split("-") res_finally = float(n1) - float(n2) print(res_finally) # 27.666666666666668
2.2 命名分组: 用?P来给匹配条件分组, 后面调用时可以用\1, \2... 也可以用(?P=组名)...
# 分组 strvar = "<div>明天<是>周六</div>" # 当不清楚字符串中含有什么内容时,用.*?进行取代 lst = re.findall(r"<(.*?)>(.*?)<(.*?)>",strvar) print(lst) # [('div', '明天', '是')] # (1) 反向引用: 将第一个括号得到的值,在\1位置处再调用,将第二个括号里得到的值在\2的位置处在调用... lst = re.findall(r"<(.*?)>(.*?)<(/\1)>",strvar) print(lst) # [('div', '明天<是>周六', '/div')] strvar = "a1b2cab" obj = re.search(r"(.*?)\d(.*?)\dc\1\2",strvar) print(obj.group()) # a1b2cab # (2) 命名分组:用?P给分的小组命名,后面再调用可以继续用\1 \2...,也可以直接用(?P=组名) # 方法一 strvar = "a1b2cab" obj = re.search(r"(?P<t1>.*?)\d(?P<t2>.*?)\dc\1\2",strvar) print(obj.group()) # a1b2cab # 方法二 strvar = "a1b2cab" obj = re.search(r"(?P<t1>.*?)\d(?P<t2>.*?)\dc(?P=t1)(?P=t2)",strvar) print(obj.group()) # a1b2cab
3. match()函数: 用法与search()类似,只是在匹配条件前强制加上^, 强制以...开头, 可用于验证用户输入内容
strvar = " 天空天地" obj = re.search("天.",strvar) print(obj.group()) # 天空 obj = re.match("天.",strvar) print(obj.group()) # 报错,因字符串时空格开头的,不符合match的匹配条件
4. split(): 切割,可根据多种条件进行切割
strvar = "bob-alice&jack%roce-fahai" res = re.split("[-&%]",strvar) print(res) # ['bob', 'alice', 'jack', 'roce', 'fahai'] strvar = "0525天气晴朗0524天气多云0523天气阴" res = re.split("\d+",strvar) print(res) # ['', '天气晴朗', '天气多云', '天气阴']
5. sub()和subn(): sub()可根据多个条件进行替换,也能选择替换的次数. subn()与sub()用法一样,只是会把新字符串与替换的次数一起组成元祖返回
# sub(): 可根据多个条件进行替换,也能选择替换的次数 strvar = "bob-alice&jack%roce-fahai" res = re.sub("[-%&]","#",strvar) print(res) # bob#alice#jack#roce#fahai res = re.sub("[-%&]","#",strvar,2) print(res) # bob#alice#jack%roce-fahai 替换两次 # subn(): 与sub()用法一样,只是会把新字符串与替换的次数一起组成元祖返回 strvar = "bob-alice&jack%roce-fahai" res = re.subn("[-%&]","#",strvar) print(res) # ('bob#alice#jack#roce#fahai', 4) strvar = "bob-alice&jack%roce-fahai" res = re.subn("[-%&]","#",strvar,2) print(res) # ('bob#alice#jack%roce-fahai', 2)
6. finditer(): 匹配字符串中的内容,返回迭代器,适用于大量数据的匹配
strvar = "0525天气晴朗0524天气多云0523天气阴" it = re.finditer("\d+",strvar) obj = next(it) print(obj.group()) # 0525 obj = next(it) print(obj.group()) # 0524 strvar = "0525天气晴朗0524天气多云0523天气阴" it = re.finditer("\d+",strvar) for i in it: print(i.group()) # 0525 # 0524 # 0523
7. compile(): 指定一个统一的匹配规则,可以定义一次匹配规则,终身使用,无需反复定义,节约系统资源
pattern = re.compile("\d+") strvar = "0525天气晴朗0524天气多云0523天气阴" res = pattern.findall(strvar) # 只需传字符串 print(res) # ['0525', '0524', '0523'] str1 = "数学97语文88英语90" res = pattern.findall(str1) print(res) # ['97', '88', '90'] obj = pattern.search(str1) res = obj.group() print(res) # 97
8. 修饰符: re.I re.M re.S
# (1) re.I : 使匹配条件对大小写不敏感 strvar = "<h1>How Are You</H1>" obj = re.search("<h1>(.*?)</h1>",strvar,flags=re.I) print(obj.group()) # (2) re.M : 使每一行都可以单独匹配,影响^和$ strvar = """<h1>123</H1> <p>123</p> <div>123</div> """ res = re.findall("^<.*?>(?:.*?)<.*?>$",strvar) print(res) # [] res = re.findall("^<.*?>(?:.*?)<.*?>$",strvar,flags=re.M) print(res) # ['<h1>123</H1>', '<p>123</p>', '<div>123</div>'] # (3) re.S: 匹配包括换行符在内的所有字符 strvar = """give 1112233mefive """ pattern = re.compile(".+five") res = pattern.findall(strvar) print(res) # ['1112233mefive'] pattern = re.compile(".+five",flags=re.S) res = pattern.findall(strvar) print(res) # ['give\n1112233mefive']
9. 正则案例之正则计算器:
strvar = "1-2*((60-30+(-40/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))" import re def sign_exp(strvar): # 式子的优化 strvar = strvar.replace("++","+") strvar = strvar.replace("--","+") strvar = strvar.replace("+-","-") strvar = strvar.replace("-+","-") return strvar def calc_exp(strvar): # 乘法除法的计算 if "*" in strvar: a, b = strvar.split("*") return float(a) * float(b) if "/" in strvar: a, b = strvar.split("/") return float(a) / float(b) def opreate_exp(strvar): # (40+5*-2+3/2) while True: obj = re.search(r"\d+(\.\d+)?[/*][+-]?\d+(\.\d+)?",strvar) # 找出式子里第一个乘法或者除法式子 if obj: res1 = obj.group() res2 = calc_exp(res1) # 计算乘法或者除法的结果 strvar = strvar.replace(res1,str(res2)) # 将结果替换进式子里 else: break strvar = sign_exp(strvar) # 优化式子 lst = re.findall("[+-]?\d+(?:\.\d+)?",strvar) # 做加减法的累加 total = 0 for i in lst: total += float(i) return total # 返回结果 def remove_bracket(strvar): # strvar = "-30+(40+5*-2+3/2)*8+(4-7)" while True: obj = re.search("\([^()]+\)",strvar) # 匹配出括号里的式子 if obj: res1 = obj.group() # print(res1) # (40+5*-2+3/2) res2 = opreate_exp(res1) # 做乘除法 strvar = strvar.replace(res1,str(res2)) # 将乘除法的结果替换进字符串 else: return strvar # 返回没有括号的字符串 def main(strvar): strvar = strvar.replace(" ","") # 将字符串里空格去掉 strvar = remove_bracket(strvar) # 清除小括号 return opreate_exp(strvar) res = main(strvar) print(res)