前言
- 贪婪模式的量词,也叫
匹配优先
量词,包括:
“?”、“*”、“+”、“{n,}”、“{n,m}” - 非贪婪模式的量词,也叫
忽略优先
量词(匹配优先量词后加“?”),包括:
“??”、“*?”、“+?”、“{n,}?”、“{n,m}?”
概述
贪婪模式和非贪婪模式影响的是被量词修饰的子表达式的行为,贪婪模式是在整个表达式匹配成功的前提下尽可能多的匹配,非贪婪模式则是在整个表达式匹配成功的前提下尽可能少的匹配。非贪婪模式只被部分NFA引擎所支持。
参照以下例子来简单认识下贪婪和非贪婪模式下不同的匹配结果:
示例字符串:“<div><img src=”http://guoweitang.net/1.jpg”><img src=”http://guoweitang.net/2.jpg”></div>”
- 贪婪模式:
/\bsrc=".+"/
匹配结果:src="http://guoweitang.net/1.jpg"><img src="http://guoweitang.net/2.jpg"
- 非贪婪模式:
/\bsrc=".+?"/
匹配结果:src="http://guoweitang.net/1.jpg"
匹配原理
NFA匹配原理参考正则基础之——NFA引擎匹配原理
贪婪模式
源字符串:”Regex”
正则表达式:”.*”
下面我们看一下匹配过程:
- 首先由第一个“””取得控制权,匹配位置0位的“””,匹配成功,控制权交给“.*”。
- “.*”取得控制权后,由于“*”是
匹配优先
量词,在可匹配可不匹配的情况下,优先尝试匹配。从位置1处的“R”开始尝试匹配,匹配成功,继续向右匹配,直到匹配到结尾的“””,匹配成功,由于此时已匹配到字符串的结尾,所以“.*”结束匹配,将控制权交给正则表达式最后的“””。 - “””取得控制权后,由于已经在字符串结束位置,匹配失败,向前查找可供回溯的状态,控制权交给“.*”,由“.*”让出一个字符,也就是字符串结尾处的“””,再把控制权交给正则表达式最后的“””,由“””匹配字符串结尾处的“””,匹配成功。
此时整个正则表达式匹配成功,其中“.*”匹配的内容为“Regex”,匹配过程中进行了一次
回溯。
非贪婪模式
源字符串:”Regex”
正则表达式:”.*?”
下面我们看一下匹配过程:
- 首先由第一个“””取得控制权,匹配位置0位的“””,匹配成功,控制权交给“.*?”。
- “.*?”取得控制权后,由于“*?”是
忽略优先
量词,在可匹配可不匹配的情况下,优先尝试不匹配。从位置1处尝试忽略匹配,也就是不匹配任何内容,将控制权交给正则表达式最后的“””。 - “””取得控制权后,从位置1处尝试匹配,由“””匹配位置1处的“R”,匹配失败,向前查找可供回溯的状态,控制权交给“.*?”,由“.*?”匹配一个字符,匹配位置1处的“R”,再把控制权交给正则表达式最后的“””。
- “””取得控制权后,从位置2处尝试匹配,由“””匹配位置1处的“e”,匹配失败,向前查找可供回溯的状态,重复以上过程,直到由“.*?”匹配到“x”为止,再把控制权交给正则表达式最后的“””。
- “””取得控制权后,从位置6处尝试匹配,由“””匹配字符串最后的“””,匹配成功。
此时整个正则表达式匹配成功,其中“.*?”匹配的内容为“Regex”,匹配过程中进行了五次
回溯。
匹配效率
从以上匹配过程看来,贪婪模式比非贪婪模式进行了更少的回溯,也就是更有效率,那是否贪婪模式逗比非贪婪模式效率更高呢,答案是否定的,如果贪婪模式匹配到了过多匹配结果不需要的内容时会进行更多的回溯,所以它们之间的效率没有绝对,要根据实际情况进行选择。
还有一个事实就是,非贪婪模式可以实现的,通过优化量词修饰的子表达式的贪婪模式都可以实现;而贪婪模式可以实现的,非贪婪模式不一定能实现。
贪婪模式效率提升
可以通过固化分组
或占有优先量词
来提升贪婪模式的匹配效率,非贪婪模式没有这一功能。
.NET中可以使用固化分组,Java中可以使用占有优先量词。