正则匹配的贪婪模式与懒惰模式
正则匹配的数量符号看上去很简单(?
, +
, *
等),就是匹配命中的数量,但实际使用时总会出现意想不到的结果。例如国外是没有书名号的,我们想把国外的双引号"book"
换成书名号《book》
。我们马上会想到:/".+"/g
,其中.
用于匹配任意字符,这个加号+
就是数量词,意思是匹配一个或无限个,g
是全文搜索。例如'"Hello, world"'.match(/".+"/g)
,就能匹配上"Hello world"
。但尝试有多个双引号时,例如a "witch" and her "broom" is one
,结果("witch" and her "broom"
)就不是我们期待的那样,实际上我们想要"witch"
和"broom"
。
贪婪模式
正则匹配使用以下方式来找到匹配:
1、从左边开始尝试匹配表达式
2、如果不匹配就向右尝试一下个字符
这些看起很表浅的文字不好解释为什么匹配不上,我们尝试一下用这个例子来解释:".+"
1、第一个表达式符是"
正则表达式引擎会从第0个字符开始匹配,字符串a "witch" and her "broom" is one
第一个字符a
明显不匹配,于是跳过,尝试下一个 空格,也不对,继续向右尝试。
2、之后,在第2个字符时匹配上了,于是会记住"
字符,这个"
就结束匹配了,接着就是.+
了。
3、第二个正式表达式是.+
,.
表示除了换行符之外的任意字符,+
表示匹配一个或无数个。所以,接下来的所有字符都能匹配上了。
4、但表达式的未尾还有一个"
,所以匹配还没结束,引擎会向左边往回搜索"
双引号,直到匹配到就停止所有匹配。
以上就是贪婪模式的算法,大概意思就是:只要匹配上的,尽可能地保留符合的字符。
懒惰模式
与贪婪模式不同,懒惰模式是尽可能少去匹配上,算法上与贪婪模式有些相似。上面的例子:".+"
要改成懒惰模式就会变成这样:".+?"
。
1、前面的没什么区别,都是首先找到"
双引号
2、接下来就不一样了,除了要匹配下一个.
(任意字符)之外,还会关注下下个字符是否匹配"
(双引号)。
3、所以最后只会终于止第二个双引号
懒惰模式问号后面不接表达式
如果懒惰模式后不接表达式,就会尽早地结束匹配,例如下面的例子:
"123 456".match(/\d+ \d+?/);
// 123 4
空格后面可以匹配上\d
,但因为问号后面没接表达式,于是就只匹配一个字符 4
就结束了(尽可能偷懒的感觉)。
其它可选的表达式
除了用懒模模式去找出书名外,还可以选用『非集合』,例如:
let regexp = /"[^"]+"/g;
let str = 'a "witch" and her "broom" is one';
str.match(regexp); // ['"witch"', '"broom"']
这个[^"]+
就表示非"
双引号的字符都能匹配上,并且+
表示尽可能的匹配上。
reference
https://javascript.info/regexp-greedy-and-lazy