正则表达式(Regluar Expressions)又称规则表达式,这个概念最初是由Unix中的工具软件(如sed 和 grep)普及开的,正则表达式在代码中常简写为RES,它本质上是一个小巧的、高度专用的编程语言,许多程序设计语言都支持通过正则表达式进行字符串操作,通俗的来讲,正则就是用一些具有特殊含义的符号组合到一起来描述字符或者字符串的方法,正则模块内嵌在Python中,并通过re模块实现,正则表达式模式被编译成一系列的字节码,然后由用C编写的匹配引擎执行.
通用字符匹配
语法 |
通配符匹配作用解析 |
. |
默认匹配除\n 之外的任意一个字符,若指定flag=DOTALL则匹配任意字符,包括换行 |
\ |
转义字符,通常情况下使后一个字符改变原来的意思,也就是脱意字符 |
[x..y] |
字符集(字符类),此参数用来指定一个字符查找范围 |
\A |
只从字符开头匹配,如果使用·re.search("\Aabc","alexabc") 则匹配不到的结果 |
\Z |
只匹配字符结尾,匹配字符结尾是指定数据的字符串,同$ 符号作用相同 |
\d |
匹配数字,范围是[0-9] ,匹配0-9中的任意数字 |
\D |
匹配非数字,范围是[^\d] ,匹配除了数字以外的其他字符 |
\w |
匹配数字或字符,匹配范围[A-Za-z0-9] |
\W |
匹配非字符或数字,匹配范围非[A-Za-z0-9] |
s |
匹配空白字符,例如匹配re.search("\s+","ab\tc1\n3").group() 结果为'\t' |
正则符号(.
): 匹配除了换行符以外的任意一个字符,一个点只代表一个字符.
>>> 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\t lyshark\n").group() 'lo\t' >>> re.search("\\t","hello\t lyshark\n").group() '\t' >>> re.search("\t","hello\t lyshark\n").group() '\t' >>> 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'
|
## 通用边界匹配
语法 |
通配符匹配作用解析 |
. |
默认匹配除\n 之外的任意一个字符,若指定flag=DOTALL 则匹配任意字符,包括换行 |
^ |
匹配以指定字符开头的数据,search(r”^a”,”\nabc\neee”,flags=re.MULTILINE) |
$ |
匹配以指定字符结尾的数据,search(“foo$”,”bfoo\nsdfsf”,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[a-zA-Z]+)(?P[0-9]+)”,”lyshark22”).groupdict(“temp”) |
正则符号(^$
): ^
匹配指定字符开头的数据,$
匹配指定字符结尾的数字.
>>> 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\nworld") <re.Match object; span=(6, 11), match='world'> >>> re.search(r"world$","hello\nworld").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)
>>> origin = "hello alex bcd abcd lge acd 19" >>> >>> ret = re.match("h\w+",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())
|
regex.search(): 搜索整个字符串去匹配第一个符合条件的数据,未匹配成功返回None.
>>> origin = "hello alex bcd abcd lge acd 19" >>> >>> re.search("^h\w+",origin).group() 'hello' >>> re.search("a\w+",origin).group() 'alex' >>> re.search("(?P<name>a\w+)",origin).groupdict() {'name': '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("al\w+",origin) ['alex'] >>> re.findall("a\w+",origin) ['alex', 'abcd', 'acd']
|
regex.sub(): 先匹配查找结果,然后进行字串的替换,也就是替换匹配成功的指定位置字符串.
sub(pattern,repl,string,count=0,flags=0)
>>> origin = "hello alex bcd abcd lge acd 19"
>>> re.sub("a[a-z]+","999999",origin,1) 'hello 999999 bcd abcd lge acd 19' >>> re.sub("a[a-z]+","999999",origin,2) 'hello 999999 bcd 999999 lge acd 19'
|
regex.split(): 字符串切割函数,用来实现对指定字符串的分割工作,根据正则匹配分割字符串.
split(pattern,string,maxsplit=0,flags=0)
>>> origin = "hello alex bcd abcd lge acd 19"
>>> re.split("alex",origin,1) ['hello ', ' bcd abcd lge acd 19'] >>> re.split("(alex)",origin,1) ['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(): print(re.match(r'.*', 'abc\nedf').group()) print('*' * 80) print(re.match(r'.*', 'abc\nedf',re.DOTALL).group())
if __name__ == '__main__': re_pattern_syntax()
|
re.MULTILINE
import re
def re_pattern_syntax1(): print(re.findall(r'^abc', 'abc\nedf')) print('*' * 80) print(re.findall(r'^abc', 'abc\nabc',re.MULTILINE))
def re_pattern_syntax2(): print(re.findall(r'abc\d$', 'abc1\nabc2')) print('*' * 80) print(re.findall(r'abc\d$', 'abc1\nabc2',re.MULTILINE))
if __name__ == '__main__': re_pattern_syntax1() re_pattern_syntax2()
|
?非贪婪模式
import re
def re_pattern_syntax4(): 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(): 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(): 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(): 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("h\w+", 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())
|
re.search()
r = re.search("a\w+", 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())
|
re.findall()
r = re.findall("a\w+",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')) print(re.search(r'(\d+)-([a-z])','34324-dfsdfs777-hhh').group(0)) print(re.search(r'(\d+)-([a-z])','34324-dfsdfs777-hhh').group(1)) print(re.search(r'(\d+)-([a-z])','34324-dfsdfs777-hhh').group(2)) print(re.search(r'(\d+)-([a-z])','34324-dfsdfs777-hhh').group(3)) print(re.search(r"(jason)kk\1","xjasonkkjason").group()) print(re.search(r'(\d)gg\1','2j333gg3jjj8').group())
print(re.search(r'(\d)gg\1','2j333gg7jjj8')) print(re.search(r'(?P<first>\d)abc(?P=first)','1abc1'))
r=re.match('(?P<n1>h)(?P<n2>\w+)','hello,hi,help') print(r.group()) print(r.groups()) print(r.groupdict())
origin ="hello alex,acd,alex" print(re.findall(r'(a)(\w+)(x)',origin)) print(re.findall(r'a\w+',origin)) print(re.findall(r'a(\w+)',origin)) print(re.findall(r'(a\w+)',origin)) print(re.findall(r'(a)(\w+(e))(x)',origin))
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')) print(re.findall(r'(\w)(\w)(\w)(\w)','alex')) origin='hello alex sss hhh kkk' print(re.split(r'a(\w+)',origin)) print(re.split(r'a\w+',origin))
|