正则表达式(Regluar Expressions)又称规则表达式,这个概念最初是由Unix中的工具软件(如sed 和 grep)普及开的,正则表达式在代码中常简写为RES,它本质上是一个小巧的、高度专用的编程语言,许多程序设计语言都支持通过正则表达式进行字符串操作,通俗的来讲,正则就是用一些具有特殊含义的符号组合到一起来描述字符或者字符串的方法,正则模块内嵌在Python中,并通过re模块实现,正则表达式模式被编译成一系列的字节码,然后由用C编写的匹配引擎执行.
## 通用字符匹配
语法 | 通配符匹配作用解析 |
---|---|
. | 默认匹配除
之外的任意一个字符,若指定flag=DOTALL则匹配任意字符,包括换行 |
转义字符,通常情况下使后一个字符改变原来的意思,也就是脱意字符 | |
[x..y] | 字符集(字符类),此参数用来指定一个字符查找范围 |
A | 只从字符开头匹配,如果使用·re.search("Aabc","alexabc") 则匹配不到的结果 |
只匹配字符结尾,匹配字符结尾是指定数据的字符串,同$ 符号作用相同 |
|
d | 匹配数字,范围是[0-9] ,匹配0-9中的任意数字 |
D | 匹配非数字,范围是[^d] ,匹配除了数字以外的其他字符 |
w | 匹配数字或字符,匹配范围[A-Za-z0-9] |
W | 匹配非字符或数字,匹配范围非[A-Za-z0-9] |
s | 匹配空白字符,例如匹配re.search("s+","ab c1
3").group() 结果为' ' |
正则符号(.
): 匹配除了换行符以外的任意一个字符,一个点只代表一个字符.
>>> import re
>>> re.search("he.o","helo hello helllo hellllo").group()
'helo'
>>> re.search("he...o","helo hello helllo hellllo").group()
'helllo'
>>> re.findall("he.o","helo heao hello helllo hellllo")
['helo', 'heao']
正则符号(): 匹配数据时,需要转义的使用会用到,这个转义字符.
>>> re.search("..\t","hello lyshark
").group()
'lo '
>>> re.search("\t","hello lyshark
").group()
' '
>>> re.search(" ","hello lyshark
").group()
' '
>>> re.search(r"\","hello\lyshark").group()
'\'
正则符号([]
): 匹配查找指定的数据范围,通常使用[0-9] [a-z] [A-Z]
这几个匹配格式.
>>> re.search("[0-9]","hello 1,2,3,4,5").group()
'1'
>>> re.findall("[0-9]","hello 1,2,3,4,5")
['1', '2', '3', '4', '5']
>>> re.search("[^0-9]","hello 1,2,3,4,5").group()
'h'
>>> re.search("[a-z]","hello 1,2,3,4,5").group()
'h'
>>> re.search("^[a-z]","hello 1,2,3,4,5").group()
'h'
>>> re.search("[^a-z]","hello 1,2,3,4,5").group()
' '
>>> re.search("[^A-Z]","hello 1,2,3,4,5").group()
'h'
## 通用边界匹配
语法 | 通配符匹配作用解析 |
---|---|
. | 默认匹配除
之外的任意一个字符,若指定flag=DOTALL 则匹配任意字符,包括换行 |
^ | 匹配以指定字符开头的数据,search(r"^a"," abc eee",flags=re.MULTILINE) |
$ | 匹配以指定字符结尾的数据,search("foo$","bfoo sdfsf",flags=re.MULTILINE).group() |
* | 匹配* 号前的字符0次或多次,findall("ab*","cabb3abcbbac") 结果['abb', 'ab', 'a'] |
+ | 匹配前一个字符1次或多次,findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb'] |
? | 匹配前一个字符1次或0次,findall("ab?","ab,a,abc,abcde") 结果['ab', 'a', 'ab', 'ab'] |
竖线 | 匹配选择竖线左边或右边的字符,search("abc竖线ABC","ABCBabcCD").group() 结果['ABC'] |
{m} | 匹配前一个字符出现过m次,search("hello{2}","hello,helloo).group() 结果['helloo'] |
{n,m} | 匹配前一个字符,最少出现过n次,最多出现过m次 |
(?P |
分组匹配,search("(?P |
正则符号(^$
): ^
匹配指定字符开头的数据,$
匹配指定字符结尾的数字.
>>> re.search(r"^h","hello world")
<re.Match object; span=(0, 1), match='h'>
>>> re.search(r"^h","hello world").group()
'h'
>>> re.search(r"world$","hello
world")
<re.Match object; span=(6, 11), match='world'>
>>> re.search(r"world$","hello
world").group()
'world'
正则符号(*
): 匹配星号前面的字符出现0次,或任意多次.
>>> re.findall("ab*","abccba23acbcabb")
['ab', 'a', 'a', 'abb']
正则符号(+
): 匹配加号前面的字符出现过1次,或任意多次,至少出现一次.
>>> re.findall("ab+","abccba23acbcabb")
['ab', 'abb']
正则符号(?
): 匹配前一个字符出现过1次,或0次,允许出现0次.
>>> re.findall("ab?","ab,abc,abb,abcd,a,acd,abc")
['ab', 'ab', 'ab', 'ab', 'a', 'a', 'ab']
>>> re.findall("ab?","ab,a,abc,abcde")
['ab', 'a', 'ab', 'ab']
正则符号(|
): 匹配选择竖线左边,或者右边的任意一种情况.
>>> re.search("abc|ABC","ABCBabcCD").group()
'ABC'
>>> re.findall("abc|ABC","ABCBabcCD")
['ABC', 'abc']
正则符号(x{m}
): 匹配前一个字符x,出现过m次的行.
>>> re.search("hello{2}","hello,helloo,hellooo,helloooo").group()
'helloo'
>>> re.search("hello{3}","hello,helloo,hellooo,helloooo").group()
'hellooo'
正则符号(x{n,m}
): 匹配前一个字符x,最少出现过n次,最多出现过m次.
>>> re.search("hello{1,2}","hello,helloo,hellooo,helloooo").group()
'hello'
>>> re.findall("hello{1,2}","hello,helloo,hellooo,helloooo")
['hello', 'helloo', 'helloo', 'helloo']
正则符号((?P<name>...)
): 匹配相关条件并自动分组,并打印出结果,其中?P<..>
是固定写法,后面紧跟正则规则.
>>> number = "371481199306143242"
>>> re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})",number).groupdict()
{'province': '3714', 'city': '81', 'birthday': '1993'}
>>> re.search("(?P<name>[a-zA-Z]+)(?P<age>[0-9]+)","lyshark22").groupdict("temp")
{'name': 'lyshark', 'age': '22'}
## 常用匹配函数
函数与方法名 | 通配符匹配作用解析 |
---|---|
regex.match | 从字符串开头位置匹配查找,如果0个或多个字符被匹配则返回相应的匹配对象,如果不匹配则返回None. |
regex.search | 扫描整个字符串,查找正则匹配到的字串中第一次出现的位置,并返回相应的匹配对象,如果匹配失败则返回None. |
regex.findall | 搜索字符串中与正则表达式匹配的所有子串,也就是查找字符串中所有的匹配结果,并且以列表的形式返回数据. |
regex.sub | 字符串的替换,简单来说就是替换字符串中与正则表达式匹配的指定数量的子串,最后返回替换修改后的字符串. |
regex.split | 以正则表达式匹配的字符串作为分隔符,对一个字符串进行分割,以列表形式返回分割后的各个字符串. |
match.expand | 通过得到的匹配对象来构造并返回一个新的字符串,未被匹配到的分组将被替换为一个空字符串. |
match.group | 返回一个或多个指定捕获组所匹配到的内容,如果只有1个参数则返回单独的字符串,多参数返回元组. |
match.groups | 返回一个包含所有分组所匹配内容的元组,如果某个分组没有匹配到内容,则取defalult所指定的值. |
match.groupdict | 返回一个包含所有命名分组名称及其所匹配内容的字典对象,如果某个分组没有匹配到内容则取默认值. |
regex.match(): 从起始位置开始匹配,匹配成功返回一个对象,未匹配成功返回None.
match(pattern,string,flags=0)
# pattern: 正则模型
# string : 要匹配的字符串
# falgs : 匹配模式
#------------------------------------------------
# 未分组情况下.
>>> origin = "hello alex bcd abcd lge acd 19"
>>>
>>> ret = re.match("hw+",origin)
>>> print(ret.group()) #获取匹配到的所有结果
>>> print(ret.groups()) #获取模型中匹配到的分组结果
>>> print(ret.groupdict()) #获取模型中匹配到的分组结果
#------------------------------------------------
# 有分组情况下. 提取匹配成功的指定内容(先匹配成功全部正则,再匹配成功的局部内容提取出来)
>>> ret = re.match("h(w+).*(?P<name>d)$",origin)
>>> print(r.group()) #获取匹配到的所有结果
>>> print(r.groups()) #获取模型中匹配到的分组结果
>>> print(r.groupdict()) #获取模型中匹配到的分组中所有执行了key的组
regex.search(): 搜索整个字符串去匹配第一个符合条件的数据,未匹配成功返回None.
>>> origin = "hello alex bcd abcd lge acd 19"
>>>
>>> re.search("^hw+",origin).group() #匹配开头是h的后面是任意字符的
'hello'
>>> re.search("aw+",origin).group() #匹配a开头后面是任意字符的
'alex'
>>> re.search("(?P<name>aw+)",origin).groupdict()
{'name': 'alex'} #分组匹配并过滤出alex
>>> re.search("(?P<姓名>[a-zA-Z]+)(?P<年龄>[0-9]+)","lyshark22").groupdict()
{'姓名': 'lyshark', '年龄': '22'} #匹配字符串,并分组打印出结果
regex.findall(): 获取非重复的匹配列表,如果有一个组则以列表形式返回,且每一个匹配均是字符串,空的匹配也会包含在结果中.
>>> origin = "hello alex bcd abcd lge acd 19"
>>> re.findall("alw+",origin)
['alex'] #匹配到单个结果,则以单列表返回
>>> re.findall("aw+",origin)
['alex', 'abcd', 'acd'] #匹配到多个结果,则以列表形式返回
regex.sub(): 先匹配查找结果,然后进行字串的替换,也就是替换匹配成功的指定位置字符串.
sub(pattern,repl,string,count=0,flags=0)
# pattern: 正则模型
# repl : 要替换的字符串或可执行对象
# string : 要匹配的字符串
# count : 指定匹配个数
# flags : 匹配模式
>>> origin = "hello alex bcd abcd lge acd 19"
>>> re.sub("a[a-z]+","999999",origin,1) #匹配以a开头则字串,并替换成9999,替换1次
'hello 999999 bcd abcd lge acd 19'
>>> re.sub("a[a-z]+","999999",origin,2) #匹配以a开头则字串,并替换成9999,替换2次
'hello 999999 bcd 999999 lge acd 19'
regex.split(): 字符串切割函数,用来实现对指定字符串的分割工作,根据正则匹配分割字符串.
split(pattern,string,maxsplit=0,flags=0)
# pattern: 正则模型
# string : 要匹配的字符串
# maxsplit:指定分割个数
# flags : 匹配模式
>>> origin = "hello alex bcd abcd lge acd 19"
>>> re.split("alex",origin,1) #无分组切割
['hello ', ' bcd abcd lge acd 19']
>>> re.split("(alex)",origin,1) #有分组,以alex最为分隔符,切割字符串
['hello ', 'alex', ' bcd abcd lge acd 19']
常用匹配: 下面举几个小例子,分别用来匹配IP地址,手机号,和邮箱地址.
IP:
^(25[0-5]|2[0-4]d|[0-1]?d?d)(.(25[0-5]|2[0-4]d|[0-1]?d?d)){3}$
手机号:
^1[3|4|5|8][0-9]d{8}$
邮箱:
[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(.[a-zA-Z0-9_-]+)+
## Flage 标识位
re.DOTALL
# 正则表达式默认以单行开始匹配的
import re
def re_pattern_syntax():
# .表示任意单一字符
# *表示前一个字符出现>=0次
# re.DOTALL就可以匹配换行符
,默认是以行来匹配的
print(re.match(r'.*', 'abc
edf').group())
print('*' * 80)
print(re.match(r'.*', 'abc
edf',re.DOTALL).group())
if __name__ == '__main__':
re_pattern_syntax()
re.MULTILINE
# 正则表达式默认以单行开始匹配的
import re
def re_pattern_syntax1():
# ^表示字符串开头(单行)
# re.MULTILINE多行匹配字符串开头
print(re.findall(r'^abc', 'abc
edf'))
print('*' * 80)
print(re.findall(r'^abc', 'abc
abc',re.MULTILINE))
def re_pattern_syntax2():
# $表示字符串结尾
# re.MULTILINE表示行的结束
print(re.findall(r'abcd$', 'abc1
abc2'))
print('*' * 80)
print(re.findall(r'abcd$', 'abc1
abc2',re.MULTILINE))
if __name__ == '__main__':
re_pattern_syntax1()
re_pattern_syntax2()
?非贪婪模式
import re
def re_pattern_syntax4():
# greedy贪婪/non-greedy非贪婪,默认的是贪婪的匹配
s = '<H1>title</H1>'
print(re.match(r'<.+>', s).group()) #贪婪模式会匹配尽量多的匹配
print(re.match(r'<.+?>', s).group()) #非贪婪模式匹配尽量少的匹配
print(re.match(r'<(.+)>', s).group(1))
print(re.match(r'<(.+?)>', s).group(1))
def re_pattern_syntax5():
# {m}/{m,}/{m,n}
print(re.match(r'ab{2,4}', 'abbbbbbb').group()) #贪婪模式尽量匹配多
print(re.match(r'ab{2,4}?', 'abbbbbbb').group()) #非贪婪模式尽量匹配少
print('*' * 80)
if __name__ == '__main__':
re_pattern_syntax4()
re_pattern_syntax5()
re.I/re.IGNORECASE
import re
def re_pattern_flags():
# re.I/re.IGNORECASE
print(re.match(r'(Name)s*:s*(w+)','NAME : Joey',re.IGNORECASE).groups())
print('*' * 80)
if __name__ == '__main__':
re_pattern_syntax_meta_char()
re.VERBOSE
import re
def re_pattern_flags1():
# re.VERBOSE此标识位可以添加注释/re.compile
s = 'the number is 20.5'
r = re.compile(r'''
d+ # 整数部分
.? # 小数点,可能包含也可能不包含
d* # 小数部分,可选
''',re.VERBOSE)
print(re.search(r,s).group())
print(r.search(s).group())
print('*' * 80)
if __name__ == '__main__':
re_pattern_syntax_meta_char1()
## 有无分组比较
re.mach()
# 无分组
r = re.match("hw+", origin)
print(r.group()) # 获取匹配到的所有结果
print(r.groups()) # 获取模型中匹配到的分组结果
print(r.groupdict()) # 获取模型中匹配到的分组结果
# 有分组
# 为何要有分组?提取匹配成功的指定内容(先匹配成功全部正则,再匹配成功的局部内容提取出来)
r = re.match("h(w+).*(?P<name>d)$", origin)
print(r.group()) # 获取匹配到的所有结果
print(r.groups()) # 获取模型中匹配到的分组结果
print(r.groupdict()) # 获取模型中匹配到的分组中所有执行了key的组
re.search()
# 无分组
r = re.search("aw+", origin)
print(r.group()) # 获取匹配到的所有结果
print(r.groups()) # 获取模型中匹配到的分组结果
print(r.groupdict()) # 获取模型中匹配到的分组结果
# 有分组
r = re.search("a(w+).*(?P<name>d)$", origin)
print(r.group()) # 获取匹配到的所有结果
print(r.groups()) # 获取模型中匹配到的分组结果
print(r.groupdict()) # 获取模型中匹配到的分组中所有执行了key的组
re.findall()
# 无分组
r = re.findall("aw+",origin)
print(r)
# 有分组
origin = "hello alex bcd abcd lge acd 19"
r = re.findall("a((w*)c)(d)", origin)
print(r)
re.split()
# 无分组
origin = "hello alex bcd alex lge alex acd 19"
r = re.split("alex", origin, 1)
print(r)
# 有分组
origin = "hello alex bcd alex lge alex acd 19"
r1 = re.split("(alex)", origin, 1)
print(r1)
r2 = re.split("(al(ex))", origin, 1)
print(r2)
常用例子
import re
print(re.findall(r'(d+)-([a-z])','34324-dfsdfs777-hhh')) # [('34324', 'd'), ('777', 'h')]
print(re.search(r'(d+)-([a-z])','34324-dfsdfs777-hhh').group(0)) # 34324-d 返回整体
print(re.search(r'(d+)-([a-z])','34324-dfsdfs777-hhh').group(1)) # 34324 获取第一个组
print(re.search(r'(d+)-([a-z])','34324-dfsdfs777-hhh').group(2)) # d 获取第二个组
print(re.search(r'(d+)-([a-z])','34324-dfsdfs777-hhh').group(3)) # IndexError: no such group
print(re.search(r"(jason)kk1","xjasonkkjason").group()) #1表示应用编号为1的组 jasonkkjason
print(re.search(r'(d)gg1','2j333gg3jjj8').group()) # 3gg3 1表示使用第一个组d
# 下面的返回None 为什么是空?而匹配不到3gg7,因为1的不仅表示第一组,而且匹配到的内容也要和第一组匹配到的内容相同,第一组匹配到3,第二组匹配到7 不相同所以返回空
print(re.search(r'(d)gg1','2j333gg7jjj8'))
print(re.search(r'(?P<first>d)abc(?P=first)','1abc1')) # 1abc1 声明一个组名,使用祖名引用一个组
r=re.match('(?P<n1>h)(?P<n2>w+)','hello,hi,help') # 组名的另外一种用法
print(r.group()) # hello 返回匹配到的值
print(r.groups()) # ('h', 'ello')返回匹配到的分组
print(r.groupdict()) # {'n2': 'ello', 'n1': 'h'} 返回分组的结果,并且和相应的组名组成一个字典
# 分组是从已经匹配到的里面去取值
origin ="hello alex,acd,alex"
print(re.findall(r'(a)(w+)(x)',origin)) # [('a', 'le', 'x'), ('a', 'le', 'x')]
print(re.findall(r'aw+',origin)) # ['alex', 'acd', 'alex']
print(re.findall(r'a(w+)',origin)) # ['lex', 'cd', 'lex']
print(re.findall(r'(aw+)',origin)) # ['alex', 'acd', 'alex']
print(re.findall(r'(a)(w+(e))(x)',origin)) # [('a', 'le', 'e', 'x'), ('a', 'le', 'e', 'x')]
r=re.finditer(r'(a)(w+(e))(?P<name>x)',origin)
for i in r :
print(i,i.group(),i.groupdict())
'''
[('a', 'le', 'e', 'x'), ('a', 'le', 'e', 'x')]
<_sre.SRE_Match object; span=(6, 10), match='alex'> alex {'name': 'x'}
<_sre.SRE_Match object; span=(15, 19), match='alex'> alex {'name': 'x'}
'''
print(re.findall('(w)*','alex')) # 匹配到了alex、但是4次只取最后一次即 x 真实括号只有1个
print(re.findall(r'(w)(w)(w)(w)','alex')) # [('a', 'l', 'e', 'x')] 括号出现了4次,所以4个值都取到了
origin='hello alex sss hhh kkk'
print(re.split(r'a(w+)',origin)) # ['hello ', 'lex', ' sss hhh kkk']
print(re.split(r'aw+',origin)) # ['hello ', ' sss hhh kkk']