Bootstrap

python中的正则表达式详解

一、正则表达式是什么?

(急于用正则表达式的朋友可以直接跳过这个部分)
————————————————————————
正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。
几乎每个网上教程或者博客对其定义都是这个,不过我还要啰嗦一下的,因为我在正则表达式上面费了好长的时间。
正则表达式,你就理解为有特殊用途字符串就没问题了,无非就是比字符串多了一些杂七杂八的操作而已。正则表达式不难,不难,不难,首先鼓励一下自己。

二、关于正则表达式的旗标

要用正则表达式,必须先引入正则表达式的包,import re
下文中函数的flags参数代表正则表示匹配的旗标,这些旗标都能通过使用该模块中的属性来代表,其都有:

旗标flags匹配代表
re.A or re.ASCII该旗标控制\w,\W,\b,\B,\d,\D,\s,\S只能匹配ASCII字符,而不能匹配UNICODE字符。可以在正则表达式中使用(?a)行内旗标来代表
re.DEBUG显示编译正则表达式的Debug信息,没有行内旗标
re.I(大写i) or re.IGNORECASE使用正则表示匹配时不区分大小写。对应的行内旗标时(?i)
re.L or re.LOCALE根据当前区域设置使用正则表达式匹配时不区分大小写。只对bytes模式起作用
re.M or re.MULTILINE多行模式的旗标。当指定这个旗标之后,“^”能匹配字符串的开头和每行的开头,也就是每个换行符(\n)之后;“$”能匹配字符串的结尾和每行的结尾,也就是换行符(\n)之前。行内旗标(?m)
re.S or re.DOTALL让点“.”能匹配包括换行符在内的所有字符,如果不指定该旗标,则“.”能匹配不包括换行符的所有字符。行内旗标(?s)
re.U or re.Unicode该旗标控制\w,\W,\d,\D,\s,\S能匹配所有Unicode字符。这个字符在python3中多余,因为python3默认时UNICODE
re.X or re.VERBOSE该旗标允许分行写正则表达,也允许正则表达式添加备注。行内旗标(?X)

什么是旗标呢?简而言之就是标记,不同的标记,不同的功能。

三、创建正则表达式

前面已经说过了,正则表达式你就当一个特殊的字符串对待就行了,无非就是可以匹配字符串而已。

1、正则表达式所支持的合法字符

字符解释
x字符x(x代表任何合法字符)
\uhhhh十六进制Unicode字符
\t制表符(‘\u0009’)
\n换行符
\r回车符
\f换页符
\a报警符
\eEscape符号(re包中的escape就是对特殊字符进行转义)
\cxx对应的控制符。例如,\cm匹配ctrl + m。x的取值A~Z,a ~z

2、正则表达式所支持的特殊字符

特殊字符解释
^匹配字符串的开头(需放到正则表达式开头)
$匹配字符串的结尾(需放到正则表达式结尾)
\b单词的边界,只匹配单词的前后空白
\B非单词边界,即只匹配单词
\A只匹配字符串的开头
\Z只匹配字符串的结尾,仅用于最后的结束符
*匹配前一个表达式0次或多次。例如:ab*可匹配a,ab或abbbbbbbbb等
+匹配前一个表达式1次或多次。例如:ab*可匹配ab或abbbbbbbbb等
?匹配前一个表达式0次或1次。例如:ab?可匹配a和ab
{m}精确匹配前一个表达式m次。例如
{m,}匹配前一个表达式至少m次,贪婪模式(尽可能多的匹配)
{m,n}匹配前一个表达式m到n次,贪婪模式
{m,n}?匹配前一个表达式m到n次,非贪婪模式(尽可能少的匹配)
[…]用来表示一组字符,匹配其中的单个字符。例如[abc]将会匹配a,b或c
[…]匹配一个范围,通过-表示。例如[a-z]表示匹配任何小写字母,[0-9][0-9]表示匹配任意二位数字
[^…]将匹配不在[]中的字符。例如:[^abc]将匹配除了a,b和c之外的字符。
“a|b”匹配a或b
(…)匹配括号内的表达式,作为一个整体

3、正则表达式所支持的预定义字符

预定义字符解释
.匹配除了换行符\n之外的任何字符
\w匹配字母数字及下划线,等价于[a-zA-Z0-9_]
\W匹配非字母数字及下划线,等价于[^a-zA-Z0-9_]
\s匹配任意空白字符,等价于[\t\n\r\f\v][1]
\S匹配任意非空字符,等价于[^\t\n\r\f\v]
\d匹配任意数字,等价于[0-9]
\D匹配任意非数字,等价于[^0-9]
^这个不是预定义字符,上述出现^这个的表示取非,也叫取否

先了解各种字符作用,然后再来了解怎么创建正则表达式。与其让语言叙述,不如你亲眼目睹,下文中的各种函数以及其Demo都是可用的,且易于理解。你可以从函数中了解各种字符的作用,还有怎么创建正则表达式。

四、python支持的正则表达式函数

1、了解python中re模块中都有那些可用的函数

import re
print(re.__all__)
#输出结果['match', 'fullmatch', 'search', 'sub', 'subn', 'split', 'findall', 'finditer', 'compile', 'purge', 'template', 'escape', 'error', 'Pattern', 'Match', 'A', 'I', 'L', 'M', 'S', 'X', 'U', 'ASCII', 'IGNORECASE', 'LOCALE', 'MULTILINE', 'DOTALL', 'VERBOSE', 'UNICODE']

下述函数,该函数只要返回的是_sre.SRE_Pattern对象(正则表达式),就可以用该对象的方法。该对象的方法有:

group(n):获取第n+1个匹配位置的字符串
span(n):获取第n+1个匹配字符串的位置

下述函数中,只要search()与match()返回的是_sre.SRE_Match对象,该对象的方法和属性有:
match.group([group1,…]):获取该匹配对象中指定组所匹配的字符串
match.__getitem_(g):这是match.group(g)的简化写法。由于match对象提供了__getitem_(g),因此可以使用match[g]来代替match.group(g)。
match.groups(default = None):返回match对象中所有组所匹配的字符串组成的元组
match.groupdict(default = None):返回match对象中所有组所匹配的字符串组成的字典。
match.start([group]):获取该指定组所匹配的字符串开始位置。
match.end([group]):获取该指定组所匹配的字符串结束位置。
match.span([group]):获取该匹配对象中指定的组的开始位置与结束位置

2、re.compile(pattern,flags = 0)函数

这个函数用于将正则表达式的字符串编译成_sre.SRE_Pattern对象,该对象代表了正则表达式编译之后再内存中的对象,它可以缓存并复用正则表达式字符串。如果程序多次需要使用同一个正则表达式的字符串,可以考虑它。

import re
temp = re.compile('abc')
#'abc'是正则表达式,很多人初学可能和我一样,认为编译后了,就是正则表达式,其实他本身就是一个正则表达式,只不过编译成了另外一个对象(_sre.SRE_Pattern)
p.search('www.abc.com')
re.search('abc','www.abc.com')
#上述两行程序的作用一样

那么能用re模块的函数解决问题,为什么还要compile一下呢?这不是多此一举吗?
其实不然,编译的好处就是,你已经把它放到缓存区了,代表你用它会更快,而且还能不断复用。

3、re.match(pattern,string,flags = 0)与re.search(pattern,string,flags = 0)函数

(1)re.match(pattern,string,flags = 0)从字符串开始位置进行匹配,如果匹配不成功,返回None。其中pattern是正则表达式,string表示被匹配的字符串,flags是旗标。
(2)re.search(pattern,string,flags = 0)扫描整个string字符串,并返回第一个匹配的pattern对象。

看代码,自己试就明白了。

import re
temp1 = re.match("www","www.777777.www")
temp2 = re.match("777777","www.777777")#返回None
print(temp1)#<re.Match object; span=(0, 3), match='www'>
print(temp2)#None
print(temp1.span())#(0, 3)
print(temp1.group())#'www'
temp1 = re.search("www","777777.www")
temp2 = re.search("777777","www")#返回None
print(temp1)#<re.Match object; span=(7, 10), match='www'>
print(temp2)#None
print(temp1.span())#(7, 10)
print(temp1.group())#'www'

4、re.findall(pattern,string,flags = 0)函数和re.finditer(pattern,string,flags = 0)

(1)re.findall(pattern,string,flags = 0)函数:扫描整个字符串,并返回字符串所匹配的所有字串组成的列表。
(2)re.finditer(pattern,string,flags = 0)函数:扫描整个字符串,并返回字符串所匹配的所有字串组成的迭代器。

import re
temp1 = re.findall("love","I love study, you love me.Love ni ge chui zi.",flags = re.I)#re.I忽略大小写
print(temp1)#['love', 'love', 'Love']
temp2 = re.finditer("love","I love study, you love me.Love ni ge chui zi.")#re.I忽略大小写
print(next(temp2))#<re.Match object; span=(2, 6), match='love'>
for e in temp2:
    print(str(e.span())+'--->'+e.group())#(18, 22)--->love

5、re.fullmatch(pattern,string,flags = 0)函数

该函数要求匹配整个字符串,如果匹配失败,返回None;否则返回一个_sre.SRE_Pattern对象。

import re
temp1 = re.fullmatch('ssss','SsSs',flags = re.I)
print(temp1)#<re.Match object; span=(0, 4), match='SsSs'>
temp2 = re.fullmatch('ssss','SsSs')
print(temp2)#None

6、re.sub(pattern,repl,string,count = 0,flags = 0)函数

这个是个重头戏,很常用。主要功能用来替换字符串里面的指定字符子串
pattern代表正则表达式,表示想要替换的内容。repl表示被替换的子串,话可以是函数。string代表要替换,要作用的字符串。count代表替换的次数(从头开始),如果等于0,则全部替换。flags旗标。

import re
#一个简单的例子
temp1 = '2008_09_27'
temp2  = re.sub('/','_',temp1,flags=0)
print(temp2)#2008_09_27
#一个稍微复杂的例子
def fun(matched):
    #matched就是被匹配的对象,也就是函数里面的参数pattern
    value = ">>>>>>>>>"+(matched.group('lang'))+"<<<<<<<<<<<"
    return value
s = 'kiet:你好啊!boy'
temp1 = re.sub('(?P<lang>\w+)',fun,s,flags=re.A)#>>>>>>>>>kiet<<<<<<<<<<<:你好啊!>>>>>>>>>boy<<<<<<<<<<<
print(temp1)

多看看这个就能理解上面那些特殊符号的用途了,其中(?P\w+)中,?P为正则表达是组的指定的名字。\w+表示读取字符,且可以多次读取,你可以去掉加号试试是怎么样的。

7、re.split(pattern,string,maxsplit = 0,flags = 0)函数

分割函数,主要用来对字符串进行分割。其中,pattern表示在什么位置分割,string表示要分割的字符串,maxsplit表示最多分割几次,flags是旗标。该函数返回的是一个列表(list);

import re
temp1 = '2019,09,27'
temp2  = re.split(',',temp1)
print(temp2)#['2019', '09', '27']
temp2 = re.split(',',temp1,maxsplit=1)
print(temp2)#['2019', '09,27']

8、re.purge()函数

清除正则表达式的缓存

9、re.escape(pattern)

对模式中除ASCII字符、数值、下划线(_)之外的其它字符进行转义。

import re
temp1 = 'ww.w,r!rr,ss_?ssddfa'
temp2 = re.escape(temp1)
print(temp2)#ww\.w,r!rr,ss_\?ssddfa

————————————————————————————————
注:有人会提到贪婪模式与勉强模式,其实很好理解,简而言之就是:贪婪模式匹配的尽可能多,勉强模式匹配的尽可能少。

;