忽略优先量词
正则里的忽略优先量词有 *?
,+?
,??
,也就是匹配优先量词加上一个?
,忽略优先也叫做我们平时说的惰性匹配
忽略优先量词也是DFA
和NFA
的分水岭,这个东西是NFA
里特有的,DFA
里没有的。
忽略优先量词与匹配优先量词不同的地方在于,匹配优先量词会首先尝试匹配,而忽略优先量词会先尝试忽略,在保存状态的时候也是不一样的,匹配优先量词因为会先尝试匹配,所以它的备用状态保存在该量词的后面,而忽略优先量词, 因为它会先尝试忽略, 所以状态保存在该量词的前面。
结合匹配优先量词,来看一个例子
假如我现在有这样一个字符串
hello my name is "zhangsan", and my father name is "zhangsi"
如果现在我们要匹配前面引号里面的 zhangsan
我们应该怎么做,我们好像可以直接zhangsan
,这样,但是这只是个特例,我们的初衷是为了匹配引号中的东西。
如果我们用".*"
来匹配呢?那匹配到的结果肯定是
"zhangsan", and my father name is "zhangsi"
这是为啥?
因为*
是匹配优先的量词啊,在进行匹配的时候,*
肯定是会先尝试匹配,它会将整个文本都匹配完, 当控制权来到"
手上时,它交出最后一个字符"
,此时匹配成功,所以匹配的结果是这个。
但是这不是我们想要的结果啊!那我们应该如何来匹配?答案是用忽略优先量词".*?"
在开始匹配的时候,*?
会因为是忽略优先而放弃匹配,然后控制权交给"
,发现匹配失败,回溯,接着匹配,以此往复,当匹配到"
时,匹配成功,匹配结果为 :
"zhangsan"
太好了!这是我们想要的结果!
我们发现,匹配优先量词会先贪婪的把能匹配的都匹配了,然后最后再以大局为重被迫交出几个字符,这样看好像是从后往前来匹配的,而忽略优先量词会懒惰的不想匹配,然后再以大局为重被迫匹配几个字符,在这样看好像是顺序匹配的。
假如有一篇长篇大论,然而你只想匹配开头的子串的话,你应该首先选择忽略优先量词
,如果你只是想匹配后面的子串,你应该首先选择匹配优先量词
。
不妨在看一个比较极端的例子
function targetStringFactory() {
let attachStr = "a";
let originStr = "abcd1234";
let attachArr = [];
for (let i = 0; i < 1000000; i++) {
attachArr.push(attachStr);
}
return [originStr, ...attachArr].join("");
}
let str = targetStringFactory();
console.time("greedy");
str.match(/.*[0-9]{4}/);
console.timeEnd("greedy");
console.time("lazy");
str.match(/.*?[0-9]{4}/);
console.timeEnd("lazy");
我只是想匹配开头的abcd1234
,后面跟着 1000000 个 a,这时候,我们把匹配优先量词
和忽略优先量词
最一个对比
greedy: 5.238ms
lazy: 0.113ms
发现忽略优先量词
确实比匹配优先量词
要快的多…