2021-05-14

贪婪和非贪婪

在学习python爬虫抓取的时候,使用正则表达式匹配字符串的课程中,涉及到了匹配过程中的贪婪与非贪婪模式。由于对于这个概念不了解,今天系统地学习一下
根据百度释义:贪婪与非贪婪模式影响的是被量词修饰的子表达式的匹配行为:贪婪模式在整个表达式匹配成功的前提下,尽可能多的匹配,而非贪婪模式在整个表达式匹配成功的前提下,尽可能少的匹配
属于贪婪模式的量词,也叫做匹配优先量词,包括:

{m,n}”、“{m,}”、“?”、“*”和“+

在匹配优先量词后加上“?”,即变成属于非贪婪模式的量词,也叫做忽略优先量词,包括:

{m,n}?”、“{m,}?”、“??”、“*?”和“+?

从正则语法的角度来讲,被匹配优先量词修饰的子表达式使用的就是贪婪模式,如“(Expression)+”;被忽略优先量词修饰的子表达式使用的就是非贪婪模式,如“(Expression)+?”

举一个数据抓取的🌰:
源字符串:aa<div>test1</div>bb<div>test2</div>cc
正则表达式一:<div>.*</div>
匹配结果一:<div>test1</div>bb<div>test2</div>
正则表达式二:<div>.*?</div>
匹配结果二:<div>test1</div>

匹配第一个“</div>”时整个表达式匹配成功,但根据“正则表达式一采用的是贪婪模式”原理,仍然继续向右匹配到第二个“</div>”,之后仍然继续向右,没有可匹配的子串,匹配结束。仅从应用角度分析,贪婪模式就是看到想要的,尽可能多地有多少就捡多少,除非再也没有想要的了。反之,非贪婪模式就是只要一个

前提条件:
整个表达式匹配成功
第二个🌰:
正则表达式三:<div>.*</div>bb
匹配结果三:<div>test1</div>bb
修饰“.”的仍然是匹配优先量词“*”,所以这里还是贪婪模式,前面的“<div>.*</div>”仍然可以匹配到“<div>test1</div>bb<div>test2</div>”,但是由于后面的“bb”无法匹配成功,这时“<div>.*</div>”必须让出已匹配的“bb<div>test2</div>”,以使整个表达式匹配成功。这时整个表达式匹配的结果为“<div>test1</div>bb”“<div>.*</div>”匹配的内容为“<div>test1</div>”。可以看到,在“整个表达式匹配成功”的前提下,贪婪模式才真正的影响着子表达式的匹配行为,如果整个表达式匹配失败,贪婪模式只会影响匹配过程,对匹配结果的影响无从谈起

贪婪和非贪婪模式间的转换
第三个🌰:
在实际应用中,匹配img标签的内容,需求为取得img标签中的图片地址,src=后固定为“””
源字符串:<img class="test" src="/img/logo.gif" title="测试" />
正则表达式一:<img\b.*?src="(.*?)".*?>
匹配结果中,捕获组1的内容即为图片地址。可以看到,这个例子中使用的都是非贪婪模式,而后面两个非贪婪模式都可以使用排除型字符组,将非贪婪模式转换为贪婪模式
正则表达式二:<img\b.*?src="([^"]*)"[^>]*>
后两处非贪婪模式,可以通过排除型字符组转换为贪婪模式,提高匹配效率,而“src=”前的非贪婪模式,由于要排除的是一个字符序列“src=”,而不是单独的某一个或几个字符,所以不能使用排除型字符组
正则表达式三:<img\b(?:(?!src=).)*src="([^"]*)"[^>]*>
“(?!src=).”表示这样一个字符,从它开始,右侧不能是字符序列“src=”,而“(?:(?!src=).)*”就表示符合上面规则的字符,有0个或无限多个。这样就达到排除字符序列的目的,实现的效果同排除型字符组一样,只不过排除型字符组排除的是一个或多个字符,而这种环视结构排除的是一个或多个有序的字符序列
总而言之,通配符“?”可以使贪婪模式变成非贪婪模式,可以用在”*”,”+”,”?”的后面,要求正则匹配的越少越好达到非贪婪转化的目的
“(?:(?!</?div\b).)*”这里使用的就是结合环视的贪婪模式,虽然每匹一个字符都要做很多判断,但这种判断是基于字符的,速度很快,而如果这里使用非贪婪模式,那么每次要做的就是分支结构“|”的判断了,而分支结构是非常影响匹配效率的,其代价远远高于对确定字符的判断。而另外一个原因,就是贪婪模式可以结合固化分组来提升效率,而对非贪婪模式使用固化分组却没有意义

小结:
匹配优先量词包括:“{m,n}”“{m,}”“?”“*”“+”
忽略优先量词包括:“{m,n}?”“{m,}?”“??”“*?”“+?”
贪婪模式的匹配效率较高,所有的非贪婪模式,都可以通过修改量词修饰的子表达式,转换为贪婪模式;贪婪模式可以与固化分组结合,提升匹配效率

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值