当前位置:

python 正则使用

访客 2024-01-05 475 0

什么是正则

正则很简单的哦,不要因为看到和乱码差不多的样子就望而却步了,他很有规律的。

简单来说,就是用来描述字符串格式的,比如数字就可以是[0-9],也可以是[0123456789],还可以是\d

而日常我们使用正则,则就是用这个描述格式来验证或匹配提取或批量替换用的,比如html代码,用<[^<>]*?>来匹配或删除所有标签

关于正则的入门知识,可以围观一下老顾的文章《文盲的正则表达式入门》,在这个文章里,老顾以正则各种符号使用的场景用途做了区分,和其他正则入门的教程不一样哦,当然,也没那么完整就是了。这个文章主要是用来方便理解正则的。

在python中使用正则

大部分的开发语言中,都有对正则的支持,毕竟,这也是字符验证的一个主要流派了,甚至不少数据库、命令行都有正则的支持,比如linux系统,比如mysql、oracle数据库等,所以正则还是有必要学一学的。

在python中,正则的内容则放在re这个包里,使用也很简单,只要引用这个包就可以了

importreprint(re.findall('\d','12345678/*9'))#一个简单的例子,找出字符串中所有数字


在python的re包里,他提供了不少的指令,咱们来一个一个地查看并学习,为了不遗漏内容,咱们把re.py这个文件打开,照着这个文档的内容来进行。文件位置如图,在python安装路径下的lib文件夹内。

一些正则的定义

在re.py这个文件中,从第29行开始,他简单的介绍了一些基本的特殊符号定义

Thespecialcharactersare:特殊字符为:"."Matchesanycharacterexceptanewline.匹配除换行符以外的任何字符。"^"Matchesthestartofthestring.匹配字符串的开头。"$"Matchestheendofthestringorjustbeforethenewlineattheendofthestring.匹配字符串的末尾或位于的换行符之前新行的末端。"*"Matches0ormore(greedy)repetitionsoftheprecedingRE.Greedymeansthatitwillmatchasmanyrepetitionsaspossible.匹配0个或多个(默认是贪婪模式)由前边的正则片段定义的重复内容。贪婪模式意味着它会匹配尽可能多的重复。""Matches1ormore(greedy)repetitionsoftheprecedingRE.匹配1个或多个(默认是贪婪模式)由前边的正则片段定义的重复内容。"?"Matches0or1(greedy)oftheprecedingRE.匹配0个或1个(默认是贪婪模式)由前边的正则片段定义的内容。*?,?,??Non-greedyversionsofthepreviousthreespecialcharacters.前边三个定义的非贪婪版本。(老顾的正则入门里说过的哦,*?是长度定义,在长度定义后加?就表示非贪婪模式,这个可以引用到任何长度定义中,包括下边也有出现){m,n}MatchesfrommtonrepetitionsoftheprecedingRE.匹配至少m个,最多n个由前边正则片段定义的内容。默认是贪婪模式{m,n}?Non-greedyversionoftheabove.上边长度定义的非贪婪版本。"\\"Eitherescapesspecialcharactersorsignalsaspecialsequence.要么转义特殊字符,要么就是一个已经预定义的特殊序列。(好吧,老顾也不知道怎么准确翻译,这里的特殊序列指的就是预定义的字符集,比如\d表示数字,\n表示回车)[]Indicatesasetofcharacters.A"^"asthefirstcharacterindicatesacomplementingset.表示一组字符。(也就是老顾说的字符集,比如\d是已经预定义好的,但是是全部数字,也许我们只需要123,不需要其他数字,那么就必须自己定义字符集[123]这个样子)作为第一个字符的“^”表示一个补集。(在字符集定义中,如果^是第一个字符,则表示这个是个补集,换个说法,就是不包含之后定义的字符集的其他字符集,比如[^\d]就是所有非数字的字符)"|"A|B,createsanREthatwillmatcheitherAorB.A|B,创建一个匹配A或B的正则片段。(...)MatchestheREinsidetheparentheses.Thecontentscanberetrievedormatchedlaterinthestring.匹配括号内的正则片段。稍后可以在字符串中检索或匹配内容。(事实上,除了稍后在其他地方使用,在当前正则片段后的部分也可以使用)(?aiLmsux)Theletterssetthecorrespondingflagsdefinedbelow.字母设置下面定义的相应标志。(这个用法老顾还没试过,翻译为百度翻译的结果)(?:...)Non-groupingversionofregularparentheses.正则圆括号的非分组版本。(也就是不可引用、追溯的部分)(?P<name>...)Thesubstringmatchedbythegroupisaccessiblebyname.组匹配的子字符串可以通过名称访问。(给分组定个名字,然后通过名字访问)(?P=name)Matchesthetextmatchedearlierbythegroupnamedname.匹配前面由组名称匹配的文本。(?#...)Acomment;ignored.一个注释,忽略掉。(嗯,我讨厌写注释)(?=...)Matchesif...matchesnext,butdoesn'tconsumethestring.右断言,位置修饰,如果能匹配成功则匹配之后的正则片段,该修饰不占用匹配空间。(?!...)Matchesif...doesn'tmatchnext.右断言,与上一个修饰意思相反,如果不符合之后的正则片段,则继续匹配。(?<=...)Matchesifprecededby...(mustbefixedlength).左断言,如果前边的字符符合正则片段...(必须定长)(?<!...)Matchesifnotprecededby...(mustbefixedlength).左断言,如果前边的字符不符合正则片段...(必须定长)(?(id/name)yes|no)Matchesyespatternifthegroupwithid/namematched,the(optional)nopatternotherwise.如果具有id/name的组匹配,则匹配yes模式,否则为(可选)无模式。(同样没用过的方法)

通过这个内容,我们基本就可以确定python中,正则能完成哪些描述,这样的描述又有什么样的限制了。

再之后,从62行开始,还介绍了一些预定义字符集,这里就不翻译了,大家随便看看就好,基本都差不多。

再然后,87行开始,说明了,re这个包之中包含的各种方法,嗯,之后一个一个地讲解

以及102行开始的,正则修饰。。。。老顾最常用的就是忽略大小写修饰,这个修饰是在正则定义之外的,还有多行匹配也偶尔用用,其他的说实话,老顾基本用不上。

python正则的方法

约定,之后的所有内容,默认都已经引入了re包,即,代码中已经包含了importre

match从字符串开头匹配

Matcharegularexpressionpatterntothebeginningofastring.
将正则表达式模式与字符串的开头匹配。

语法:
match(pattern,string,flags=0)
match(正则表达式,匹配字符串,正则表达式修饰(默认为0))

需要注意,这个方法时严格从字符串开头进行匹配的,如果想在字符串中间找到匹配,不要用这个方法哦。

示例:

print(re.match('\d','123.548'))#返回结果<re.Matchobject;span=(0,3),match='123'>print(re.match('\s\d','123.548'))#返回结果,匹配失败,没有Match信息None

在python中,使用匹配返回的结果,大部分都是re.Match对象,他包含了一个span元组,这个元组就是匹配字符串对应的切片位置,match就是匹配的结果了

正则返回的结果分析(重要)
ans=re.match('\d','123.548')print(ans)<re.Matchobject;span=(0,3),match='123'>

我们通过一个变量,来分析返回的结果该如何使用

print(ans.re)#输出这个匹配的正则re.compile('\\d')#包含了我们正则片段的正则编译对象print(ans.string)#输出这个匹配的原始匹配字符串123.548print(ans.pos,ans.endpos)#输出原始字符串的切片,包括起始位置和结束位置07print(ans.regs)#输出匹配的结果,注意,这里是元组,每一个内层元组是一个匹配((0,3),)print(ans.span)#输出span方法<built-inmethodspanofre.Matchobjectat0x00000148614E76C0>print(ans.span(0))#输出第一个匹配的切片信息(0,3)print(ans.start(0),ans.end(0))#返回指定匹配顺序的切片开始和结束位置03print(ans.group(0))#返回第一个匹配结果匹配到的内容123

嗯,由于刚才的匹配,我们简单的匹配了一个\d,没有带分组信息,这次我们加上一个分组来尝试

ans=re.match('(\d)\.(\d)','123.548')#带有两个分组的正则匹配print(ans)<re.Matchobject;span=(0,7),match='123.548'>print(ans.span(0),ans.span(1),ans.span(2))#返回所有的切片信息#这里需要注意的是,如果带有分组,那么原始字符串自动占据返回结果的第一个分组,即下标为0的分组#所以,实时上我们需要提取的结果,应该是从下标1开始的(0,7)(0,3)(4,7)print(ans.group(0),ans.group(1),ans.group(2))#输出该匹配结果的分组结果123.548123548print(ans.groups())#输出该匹配的分组内容,注意,这里的groups没有参数,输入参数也是无效的#所以想要引用结果,必须使用group('123','548')print(ans.groups(101))('123','548')print(ans.expand('\\1is\\2'))#将匹配结果按照指定的字符串格式输出,这里可以引用分组结果123is548

好了,经过这几个输出,我们应该明白了正则匹配结果返回的内容都包含哪些信息了,还有个别的属性方法老顾也不太清楚,这里就不再讲解了

有了这些方法和属性,我们就可以很方便的使用正则返回结果中的内容了

fullmatch严格匹配整个字符串

Matcharegularexpressionpatterntoallofastring.
将正则表达式模式与所有字符串匹配。

语法:
fullmatch(pattern,string,flags=0)
fullmatch(正则表达式,匹配字符串,正则表达式修饰(默认为0))

print(re.fullmatch('[a-z]','0123456'))#由于没有字母,匹配失败Noneprint(re.fullmatch('[a-z]','Abcdefgh',re.IGNORECASE))#虽然忽略大小写了,但是中间有空格,不能严格匹配,匹配失败Noneprint(re.fullmatch('\d','0123456'))#整个字符串严格匹配\d<re.Matchobject;span=(0,7),match='0123456'>print(re.fullmatch('[a-z]','Abcdefgh',re.IGNORECASE))#整个字符串严格匹配[a-z]<re.Matchobject;span=(0,8),match='Abcdefgh'>print(re.fullmatch('[a-z]','Abcdefgh',re.IGNORECASE))#长度对应不上,没有长度修饰则只匹配一个字符的字符串才可以成功None

嗯,就是严格匹配字符串首尾,默认相当于search的正则前边带^后边带$

search任意位置开始匹配

Searchastringforthepresenceofapattern.
在字符串中搜索是否存在模式。

语法:
search(pattern,string,flags=0)
search(正则表达式,匹配字符串,正则表达式修饰(默认为0))

print(re.search('[a-z]','0123456'))#没有匹配到结果,返回NoneNoneprint(re.search('[a-z]','Abcdefgh',re.IGNORECASE))#返回第一个匹配成功的结果片段<re.Matchobject;span=(0,4),match='Abcd'>print(re.search('\d','abcde0123456'))#返回第一个匹配成功的结果片段<re.Matchobject;span=(5,12),match='0123456'>print(re.search('[a-z]','Abcdefgh',re.IGNORECASE))#整个字符串都符合正则,所以完整返回了<re.Matchobject;span=(0,8),match='Abcdefgh'>print(re.search('[a-z]','Abcdefgh',re.IGNORECASE))#返回第一个匹配成功的片段<re.Matchobject;span=(0,1),match='A'>

很明显,search要比match和fullmatch要宽松很多,我们可以更自由的发挥

sub替换匹配内容

Substituteoccurrencesofapatternfoundinastring.
替换字符串中发现的模式的出现次数。(百度翻译的什么鬼?)
替换字符串中所有或指定次数的符合匹配的内容。(老顾翻译)

语法:
sub(pattern,repl,string,count=0,flags=0)
sub(正则表达式,替换表达式,匹配字符串,替换次数,正则表达式修饰(默认为0))

print(re.sub('(\d{2})','\\1-','18300183000'))#将数字每两位之后加一个减号18-30-01-83-00-0----print(re.sub('(?=\d)(?<!\d)','\n','1开始2结束3运行4调试11开会12报告'))#在每个连续数字前插入一个换行,大家可以和下一个比较一下有什么不同1开始2结束3运行4调试11开会12报告----print(re.sub('(?=\d)','\n','1开始2结束3运行4调试11开会12报告'))#在每个数字前,插入一个换行1开始2结束3运行4调试11开会12报告----print(re.sub('(?=\d)(?<!\d)','\n','1开始2结束3运行4调试11开会12报告',count=5))#指定了替换次数的正则替换1开始2结束3运行4调试11开会12报告

正则替换还是很好用的,但是,这里要说一个但是,python的正则替换里描述的内容,有一个很重要的部分,很多人可能都忽略掉了。原文如下

defsub(pattern,repl,string,count=0,flags=0):"""Returnthestringobtainedbyreplacingtheleftmostnon-overlappingoccurrencesofthepatterninstringbythereplacementrepl.replcanbeeitherastringoracallable;ifastring,backslashescapesinitareprocessed.Ifitisacallable,it'spassedtheMatchobjectandmustreturnareplacementstringtobeused."""return_compile(pattern,flags).sub(repl,string,count)

replcanbeeitherastringoracallable;
repl可以是字符串,也可以是可调用的
Ifitisacallable,it’spassedtheMatchobjectandmustreturnareplacementstringtobeused.
如果是可调用的的话,它传递Match对象,并且必须返回要使用的替换字符串。

也就是说,我可以通过委托的方法,来处理匹配到的内容,并返回指定的结果
来,我们试试看哦

defstar(n):#这里的n就是一个正则匹配对象Matchreturn'*'*int(n.group(0))'\n'#将匹配到的数字转成星星的数量,并返回星星print(re.sub('\d',star,'123456789'))#委托匹配到的内容使用star方法来处理*********************************************importrandomdefstar(n):return''.join([random.choice('abcdefghijklmnopqrstuvwxyz')for_inrange(int(n.group(0)))])'\n'print(re.sub('\d',star,'123454321'))vsrlvdtrtmrnhrqloxbvjdsui

以及,老顾在《在学习爬虫的路上,有多少坑在前边》一文中,实际用到的两个转编码的函数

正则替换如此强大,你还不心动吗?

subn以元组方式返回替换结果

Sameassub,butalsoreturnthenumberofsubstitutionsmade.
与sub相同,但也返回进行的替换次数。(百度翻译持续不说人话)
和sub方法一样,但是,在元组中,记录了替换的次数。(老顾翻译)

语法:
subn(pattern,repl,string,count=0,flags=0)
subn(正则表达式,替换表达式,匹配字符串,替换次数,正则表达式修饰(默认为0))

print(re.subn('(?=\d)(?<!\d)','\n','1开始2结束3运行4调试11开会12报告'))('\n1开始\n2结束\n3运行\n4调试\n11开会\n12报告',6)#元组第一个元素为返回结果,第二个元素为替换次数defstar(n):return'*'*int(n.group(0))'\n'print(re.subn('\d',star,'13243546576879'))('*\n***\n**\n****\n***\n*****\n****\n******\n*****\n*******\n******\n********\n*******\n*********\n',14)

subn就不细说了,和sub一样的玩法

split正则切割

Splitastringbytheoccurrencesofapattern.
按出现的模式拆分字符串。

语法:
split(pattern,string,maxsplit=0,flags=0)
split(正则表达式,匹配字符串,最大切割次数(默认0,即不限制次数),正则表达式修饰(默认为0))

print(re.split('[^\d]','13243,54uu6576879'))#以非数字的字符为切割符,切割字符串['13','243','54','','6576879']#由于两个u之间没有数字,所以结果中会出现空字符串print(re.split('[^\d]','13243,54uu6576879',maxsplit=2))#指定最大切割次数的分割字符串['13','243','54uu6576879']

嗯。。。。很常用实用以及很容易用,不细说了

需要注意的是,这里返回的就是字符串数组,没有Match信息了

findall查找所有匹配结果

Findalloccurrencesofapatterninastring.
查找字符串中出现的所有模式。

语法:
findall(pattern,string,flags=0)
findall(正则表达式,匹配字符串,正则表达式修饰(默认为0))

额。。。怎么说呢,在抛弃了Match信息的情况下,以字符串数组方式返回所有匹配

print(re.findall('\d','13243,54uu6576879'))['13','243','54','6576879']

在大部分的工作中,其实这个也就够用了,因为我们并不关心他出现在什么地方,前后文有什么,但在一些需求比较复杂的时候,这个findall就不如finditer了,因为findall丢失了Match信息,而finditer还保留着Match信息

finditer返回一个迭代结果

ReturnaniteratoryieldingaMatchobjectforeachmatch.
返回一个迭代器,为每个匹配生成一个Match对象。

语法:
finditer(pattern,string,flags=0)
finditer(正则表达式,匹配字符串,正则表达式修饰(默认为0))

print(re.finditer('\d','13243,54uu6576879'))#返回了一个迭代器<callable_iteratorobjectat0x00000148615CD540>forminre.finditer('\d','13243,54uu6576879'):#使用迭代,将所有匹配输出print(m)<re.Matchobject;span=(0,2),match='13'>#第一个匹配<re.Matchobject;span=(3,6),match='243'>#第二个匹配<re.Matchobject;span=(7,9),match='54'>#第三个匹配<re.Matchobject;span=(11,18),match='6576879'>#第四个匹配

由于这里完整的保留了Match的信息,所以我们可以对匹配到的结果进行定位,以及检查前后文之类的操作,需要用的人自然会用到,也就不细说了

compile正则表达式的预编译

CompileapatternintoaPatternobject.
将模式编译为pattern对象。

语法:
compile(pattern,flags=0)

嗯。。。定义一个正则,也许复杂的正则会用到这个,老顾一般都直接写正则片段,除了上了1k的正则,老顾一般不会用这个

purge清除缓存

Cleartheregularexpressioncache.
清除正则表达式缓存。

语法:
purge()

没啥用,这是针对预编译的正则来说的,老顾基本上都是随手写正则随手用,没有预编译过

escape正则表达式格式化

Backslashallnon-alphanumericsinastring.
对正则表达式字符串中的所有非字母数字进行转义。

语法:
escape(pattern)

这个还算有点用,算是正则格式化的部分,比如我们想从字符串中提取到一部分作为匹配内容,但是匹配到的内容可能会有特殊符号,比如*号,?号之类的,这些在正则中有其他意义,所以我们需要提前将这些符号转义,escape就是干这个用的,将特殊符号都转义。老顾在c#中使用正则,还单独自己写了个FormatRegex方法,就是干这个用的。郁闷。

print(re.escape('123.456*789=$100'))#格式化正则片段,将其特殊符号都进行转义123\.456\*789=\$100

发表评论

  • 评论列表
还没有人评论,快来抢沙发吧~