正则表达式
一 介绍
拼正则表达式是什么?
- 它是约束字符串匹配某种形式的规则
正则表达式有什么用?
- 检测某个字符串是否符合规则比如:判断手机号,身份证号是否合法
- 提取网页字符串中想要的数据.比如:爬虫中,提取网站天气,信息,股票代码,星座运势等具体关键字
线测试工具http://tool.chinaz.com/regex
正则表达式由一些[普通字符]和一些[元字符]组成:
(1)普通字符包括大小写字母和数字
(2)元字符具有特殊含义,大体种类分为如下
- 预定义字符集,字符组
- 量词
- 边界符
- 分组
二 正则表达式findall
2.1 单字符匹配
findall("正则表达式","要匹配的字符串")
\d 和 \D
- \d 匹配数字
- \D 匹配非数字
import re lst = re.findall("\d","davfdardwer24523`de3r43wqe#$@!dwd@#!2143DC") print (lst) lst = re.findall("\D","davfdardwer24523`de3r43wqe#$@!dwd@#!2143DC") print (lst)
执行
[root@node10 python]# python3 test.py ['2', '4', '5', '2', '3', '3', '4', '3', '2', '1', '4', '3'] ['d', 'a', 'v', 'f', 'd', 'a', 'r', 'd', 'w', 'e', 'r', '`', 'd', 'e', 'r', 'w', 'q', 'e', '#', '$', '@', '!', 'd', 'w', 'd', '@', '#', '!', 'D', 'C']
\w和\W
- \w 匹配字母数字下换线 (正则函数中,支持中文匹配)
- \W 匹配非字母,数字,下划线
import re lst = re.findall("\w","dwer_=\|:';.你好世界24523`de3re#$@!dwd@#!2143DC") print (lst) lst = re.findall("\W","dwer_=\|:';.你好世界24523`de3re#$@!dwd@#!2143DC") print (lst)
执行
'd', 'w', 'e', 'r', '_', '你', '好', '世', '界', '2', '4', '5', '2', '3', 'd', 'e', '3', 'r', 'e', 'd', 'w', 'd', '2', '1', '4', '3', 'D', 'C'] ['=', '\\', '|', ':', "'", ';', '.', '`', '#', '$', '@', '!', '@', '#', '!']
\s和\S
- \s 匹配任意的空白符(\n \t)
- \S 匹配任意的非空白符
import re strvar = ' ' lst = re.findall("\s",strvar) print(lst) strvar = " 8989sdf 111" lst = re.findall("\S",strvar) print(lst)
执行
[root@node10 python]# python3 test.py [' ', '\t', '\t', '\t', '\t', '\t'] ['8', '9', '8', '9', 's', 'd', 'f', '1', '1', '1']
\n和\t
- \n 匹配一个换行
- \t 匹配一个缩进
import re strvar = """ sdfsd sdfsdf sfsdfsdf sdfsddffg dfgdfg fgdfg """ lst = re.findall("\n",strvar) print(lst) lst = re.findall("\t",strvar) print(lst)
字符组 [123]
[123] 必须从字符组当中挑出一个出来,个数上有要求,必须一个,如果匹配不到就是空.
import re lst = re.findall("[123]","sdfs3fkj5kj") print(lst) print(re.findall('a[abc]b','aab abb acb adb'))
执行
[root@node10 python]# python3 test.py ['3'] ['aab', 'abb', 'acb']
字符组
import re # [0123456789] => [0-9] -是一个特殊字符,代表范围0-9 0到9 print(re.findall('a[0123456789]b','a1b a2b a3b acb ayb')) print(re.findall("a[0-9]b",'a1b a2b a3b acb ayb')) #[abcdefg] => [a-g] 表达所有小写字符可以用[a-z] print(re.findall('a[a-z]b','a1b a2b a3b acb ayb adb')) #['acb', 'ayb', 'adb'] # print(re.findall('a[abcdefg]b','a1b a2b a3b acb ayb adb')) #['acb', 'adb'] # [ABCDEFG] A-G [A-Z]用来表达所有大写字符 print(re.findall('a[A-Z]b','a1b a2b a3b aAb aDb aYb')) #['aAb', 'aDb', 'aYb'] print(re.findall('a[ABCDEFG]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'] print(re.findall('a[0-9][*#/]b','a1/b a2b a29b a56b a456b')) #['a1/b'] # 字符组当中的^ 代表除了的意思 # 除了+ - * / 这些字符之外,剩下的都能匹配 print(re.findall('a[^-+*/]b',"a%b ccaa*bda&bd")) #['a%b', 'a&b'] # 在特殊字符前面加商一个\,让原本有意义的字符变得无意义,用于正则字符匹配 print(re.findall('a[\^]b',"a^b")) #['a%b', 'a&b']
执行
[root@node10 python]# python3 test.py ['a1b', 'a2b', 'a3b'] ['a1b', 'a2b', 'a3b'] ['acb', 'ayb', 'adb'] ['aAb', 'aDb', 'aYb'] ['aAb', 'aDb'] ['aab', 'aAb', 'aWb', 'aqb', 'a1b'] ['a1/b'] ['a%b', 'a&b'] ['a^b']
2.2 正则匹配多个字符
量词
import re '''1) ? 匹配0个或者1个a ''' print(re.findall('a?b','abbzab abb aab')) '''2) + 匹配1个或者多个a ''' print(re.findall('a+b','b ab aaaaaab abb')) '''3) * 匹配0个或者多个a ''' print(re.findall('a*b','b ab aaaaaab abbbbbbb')) '''4) {m,n} 匹配m个至n个a ''' print(re.findall('a{1,3}b','aaab ab aab abbb aaz aabb'))
执行
[root@node10 python]# python3 test.py ['ab', 'b', 'ab', 'ab', 'b', 'ab'] ['ab', 'aaaaaab', 'ab'] ['b', 'ab', 'aaaaaab', 'ab', 'b', 'b', 'b', 'b', 'b', 'b'] ['aaab', 'ab', 'aab', 'ab', 'aab']
2.3 贪婪匹配 和 非贪婪匹配
贪婪匹配:
- 默认向更高次数匹配,程序当中默认使用贪婪匹配
非贪婪匹配:
- 默认向更少次数匹配
- 语法:只需要在量词的后面再加上一个?号
底层用的时回溯算法:
从左到右开始寻找,直到找到最后再也找不到了,回头,找离右侧最近的那个值返回
import re strvar = "刘能和刘罗锅和刘备有3儿子4" #.表示除了\n以外的任意字符
lst = re.findall("刘.",strvar) print (lst) #.?表示刘后的字符可以出现1次或者多个
lst = re.findall("刘.?",strvar) print(lst) #.+表示刘后的的任意字符出现0个或者多个
lst = re.findall("刘.+",strvar) print(lst)
lst = re.findall("刘.*",strvar) print(lst)
lst = re.findall("刘.*子",strvar) print(lst)
lst = re.findall("刘.{1,20}",strvar) print(lst) lst = re.findall("刘.{1,20}子",strvar) print(lst) print("非贪婪匹配") lst = re.findall("刘.??",strvar) print(lst) lst = re.findall("刘.+?",strvar) print(lst) lst = re.findall("刘.*?",strvar) print(lst) lst = re.findall("刘.*?子",strvar) print(lst) lst = re.findall("刘.{1,20}?",strvar) print(lst) # 默认使用的是贪婪模式,一直找到最后一个子,如果使用非贪婪,找到第一个字就立刻返回. lst = re.findall("刘.{1,20}?子",strvar) print(lst)
执行
['刘能', '刘罗', '刘备'] ['刘能', '刘罗', '刘备'] ['刘能和刘罗锅和刘备有3儿子4子'] ['刘能和刘罗锅和刘备有3儿子4子'] ['刘能和刘罗锅和刘备有3儿子4子'] ['刘能和刘罗锅和刘备有3儿子4子'] ['刘能和刘罗锅和刘备有3儿子4子']
非贪婪匹配 ['刘', '刘', '刘'] ['刘能', '刘罗', '刘备'] ['刘', '刘', '刘'] ['刘能和刘罗锅和刘备有3儿子'] ['刘能', '刘罗', '刘备'] ['刘能和刘罗锅和刘备有3儿子']
2.4 边界符\b ^ $
例:\b \bw 以w作为左边界 d\b 以d作为右边界
\b 它默认是一个转义字符 退格 backspace
需要通过字符串前面加上r,让转义字符失效,呈现正则的含义.
import re
#只匹配右边界d lst = re.findall(r"d\b","word pwd abc") print(lst)
#匹配右边界前的任意字符 lst = re.findall(r".*d\b","word pwd abc") print(lst) # 能够匹配到一个右边界d ,就立刻返回 # .可以匹配空白符,除了\n,剩下所有字符都能匹配到 lst = re.findall(r".*?d\b","word pwd abc") print(lst)
#匹配到非空白符 lst = re.findall(r"\S*?d\b","word pwd abc") # 纯单词,舍去空格 print(lst)
#匹配以w开头的非空白符 lst = re.findall(r"\bw\S*","word pwd abc") #'word' print(lst)
执行
[root@node10 python]# python3 test.py ['d', 'd'] ['word pwd'] ['word', ' pwd'] ['word', 'pwd'] ['word']
2.5 使用^$
- 如果使用了^ 或者 $
- ^ : 必须以某个字符开头,后面剩下的无所谓
- $ : 必须以某个字符结尾,前面剩下的无所谓
- 意味着把这个字符串当成一个整体
import re strvar = "大哥大嫂大爷" print ("匹配大后面出\\n的任意字符") print(re.findall('大.',strvar)) print ("匹配以大开头后面出\n的任意字符,同时把这个字符串看成一个整体") print(re.findall('^大.',strvar)) # 只匹配一个,把字符串开成整体 print ("必须以一个任意字符结尾") print(re.findall('大.$',strvar)) # 只匹配一个,把字符串开成整体 # strvar = "大书" print ("以大开头,以任意字符结尾的两个字符,为空") print(re.findall('^大.$',strvar)) print ("以大开头,匹配.*,?只是非贪婪匹配") print(re.findall('^大.*?$',strvar)) print ("以g开头,匹配到空格停止") print(re.findall('^g.*? ' , 'giveme 1gfive gay')) print ("以e结尾,向前找到five,打出 ") print(re.findall('five$' , 'aassfive')) print ("以c结尾,匹配abc打印") print(re.findall("abc$","abcabcabc")) print ("全部卡死") print(re.findall('^giveme$' , 'giveme')) print ("完全卡死,并不匹配,返回空") print(re.findall('^giveme$' , 'giveme giveme')) print ("以g开头,e结尾,中间匹配任意") print(re.findall('^g.*?e$' , 'giveme giveme')) print ("匹配giveme,有两个") print(re.findall('giveme' , 'giveme giveme')) print ("以g开头,卡到e") print(re.findall("^g.*e",'giveme 1gfive gay')) print ("以g开头,卡到e,并使用非贪婪匹配") print(re.findall("^g.*?e",'giveme 1gfive gay'))
执行
[root@node10 python]# python3 test.py 匹配大后面出\n的任意字符 ['大哥', '大嫂', '大爷'] 匹配以大开头后面出 的任意字符,同时把这个字符串看成一个整体 ['大哥'] 必须以一个任意字符结尾 ['大爷'] 以大开头,以任意字符结尾的两个字符,为空 [] 以大开头,匹配.*,?只是非贪婪匹配 ['大哥大嫂大爷'] 以g开头,匹配到空格停止 ['giveme '] 以e结尾,向前找到five,打出 ['five'] 以c结尾,匹配abc打印 ['abc'] 全部卡死 ['giveme'] 完全卡死,并不匹配,返回空 [] 以g开头,e结尾,中间匹配任意 ['giveme giveme'] 匹配giveme,有两个 ['giveme', 'giveme'] 以g开头,卡到e ['giveme 1gfive'] 以g开头,卡到e,并使用非贪婪匹配 ['give']
2.6 正则表达式分组匹配
正常分组
- 正常情况下用()圆括号进行分组 可以用\1 反向引用第一个圆括号匹配的内容。
- (?:正则表达式) 表示取消优先显示的功能
import re print("匹配所有_good前的任意字符打印出") print(re.findall('.*?_good','wusir_good alex_good xboyww_good')) print("匹配所有的姓名") print(re.findall('(.*?)_good','wusir_good alex_good xboyww_good')) print("匹配所有的姓名,然后取消分组") print(re.findall('(?:.*?)_good','wusir_good alex_good secret男_good'))
执行
[root@node10 python]# python3 test.py 匹配所有_good前的任意字符打印出 ['wusir_good', ' alex_good', ' xboyww_good'] 匹配所有的姓名 ['wusir', ' alex', ' xboyww'] 匹配所有的姓名,然后取消分组 ['wusir_good', ' alex_good', ' secret男_good']
a|b 匹配字符a 或者 匹配字符b 把字符串长的写在前面,字符串短的写在后面
- | 代表或的意思
import re res = re.findall("abcd|abc","abcdabcdabcd abc") print(res)
执行
[root@node10 python]# python3 test.py ['abcd', 'abcd', 'abcd', 'abc']
2.7 匹配小数
import re strvar = " 3.14 .34 35. ... 2.41.3 34.." res = re.findall("\d+\.\d+",strvar) print(res)
执行
[root@node10 python]# python3 test.py ['3.14', '2.41']
匹配小数和整数
import re strvar = "3.15 34 .. 3434.23.234.2323 .34.34 .3434 .. .. nihao" res = re.findall("\d+\.\d+|\d+",strvar) print(res)
执行
[root@node10 python]# python3 test.py ['3.15', '34', '3434.23', '234.2323', '34.34', '3434']
优化版
findall 这个函数默认优先显示小括号里面的内容,如果不想显示,用?:
小括号代表的是分组,放到一起代表整体,这个例子要么出现小数点和后面的值,要么不出现.
import re strvar = "3.15 34 .. 3434.23.234.2323 .34.34 .3434 .. .. nihao" res = re.findall("\d+(?:\.\d+)?",strvar) print(res)
执行
[root@node10 python]# python3 test.py ['3.15', '34', '3434.23', '234.2323', '34.34', '3434']
匹配135或171的手机号
import re res = re.findall("(?:135|171)\d{8}","13588889897 17180668088") print(res)
执行
[root@node10 python]# python3 test.py ['13588889897', '17180668088']
正则要求卡主开头和结尾,且长度是11位 (这个例子开头结尾满足,但长度不对)
import re res = re.findall("(?:^135|^171)\d{8}$","13588889897 17180668088") print(res)
执行
[root@node10 python]# python3 test.py []
#(?: ^135 | ^171 )
import re res = re.findall("(?:^135|^171)\d{8}$","17188889897") print(res)
执行
[root@node10 python]# python3 test.py ['17188889897']
三 search 正则方法
3.1 介绍
search("正则表达式","要匹配的字符串")
它返回的是一个对象
- 对象.属性
- 对象.方法()
- 想要获取对象里面的值用group()方法
- 想要获取分组里面的值用groups()方法
和 findall区别
- findall 找出所有匹配的结果,从左到右,返回的是列表
- search 找出一个结果就返回,从左到右,返回的是对象
import re obj = re.search("\d+","ksj323rf33sdf*&*& 78787878") print(obj) res = obj.group() print(res)
执行
[root@node10 python]# python3 test.py <_sre.SRE_Match object; span=(3, 6), match='323'> 323
3.2 基本使用
匹配www.baidu.com 或者 www.google.com
import re obj = re.search("(www).(baidu|google).(com)","www.baidu.com www.google.com") res = obj.group()
#通过group拿到实际匹配的内容 print(res) #通过groups拿到小括号里面匹配的内容
res = obj.groups() print(res)
执行
[root@node10 python]# python3 test.py www.baidu.com ('www', 'baidu', 'com')
同一时间,既可以拿到匹配的结果,又可以拿到小括号里的内容,而findall不具备
findall 同一时间只能拿取一种结果,要么是实际匹配的,要么是小括号的.
search 只能匹配一个, findall 可以匹配所有. 这是两个函数之间的差别.
import re obj = re.search("(www).(baidu|google).(com)","www.baidu.com www.google.com") res = obj.group() print(res) #www.baidu.com res = obj.groups() print(res) res = re.findall("(www).(baidu|google).(com)","www.baidu.com www.google.com") print(res)
执行
[root@node10 python]# python3 test.py www.baidu.com ('www', 'baidu', 'com') [('www', 'baidu', 'com'), ('www', 'google', 'com')]
3.3 对比两个函数区别
匹配出5*4 或者 6/8
import re strvar = "5*4+6/8" obj = re.search("\d+[*/]\d+",strvar) # 获取对象当中的值 res = obj.group() print(res) res= re.findall("\d+[*/]\d+",strvar) print(res)
执行
[root@node10 python]# python3 test.py 5*4 ['5*4', '6/8']
一般有分组的情况下用search 更多,找到所有匹配条件一般用findall