RegExp 贪婪与懒惰模式

发布:elantion 日期:2019-09-30 阅读:46 评论:0

数量符号看上去很简单,但实际使用时总会出现意想不到的结果。例如国外是没有书名号的,我们想把国外的双引号"book"换成书名号《book》。我们马上会想到:/".+"/g,其中.用于匹配任意字符,这个加号+就是数量词,意思是匹配一个或无限个,g是全文搜索。例如'"Hello, world"'.match(/".+"/g),就能匹配上"Hello world"。但尝试有多个双引号时,例如a "witch" and her "broom" is one,结果就不是我们希望的那样:"witch" and her "broom"(实际上我们更想得到"witch""broom"),这是为什么呢?

贪婪模式

RegExp使用以下方式来找到匹配:
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,但因为问号后面没接表达式,于是就只匹配一个就结束了。

其它可选的表达式

除了用懒模模式去找出书名外,还有使用非集合,例如:

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