一、正则表达式是什么?
(急于用正则表达式的朋友可以直接跳过这个部分)
————————————————————————
正则表达式(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 | 报警符 |
\e | Escape符号(re包中的escape就是对特殊字符进行转义) |
\cx | x对应的控制符。例如,\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
————————————————————————————————
注:有人会提到贪婪模式与勉强模式,其实很好理解,简而言之就是:贪婪模式匹配的尽可能多,勉强模式匹配的尽可能少。