JS-正则-非贪婪模式

贪婪模式

默认情况下,所有的限定词都是贪婪模式,表示尽可能多的去捕获字符,能匹配多少就匹配多少。从正则语法的角度来讲,被匹配优先量词修饰的子表达式使用的就是贪婪模式,如/a+/
标识符:+,?,*,{n},{n,},{n,m}

非贪婪模式

限定词后增加?,则是非贪婪模式,表示尽可能少的去捕获字符。
从正则语法的角度来讲,被忽略优先量词修饰的子表达式使用的就是非贪婪模式,如/a+?/。标识符:+?,??,*?,{n}?,{n,}?,{n,m}?

1
2
3
4
5
const str = "aaab",
reg1 = /a+/, //贪婪模式
reg2 = /a+?/;//非贪婪模式
console.log(str.match(reg1)); // ["aaa"]
console.log(str.match(reg2)); // ["a"]

回溯

正则表达式中有个优先规则:最先开始的匹配拥有最高的优先权

1
2
3
4
5
6
7
/*表达式1*/ 
'aaaaaaab'.match(/a*b/);
// ["aaaaaaab", index: 0, input: "aaaaaaab", groups: undefined];

/*表达式2*/
'aaaaaaab'.match(/a*?b/);
// ["aaaaaaab", index: 0, input: "aaaaaaab", groups: undefined]l;

💡该贪婪匹配表达式在引擎中的回溯表现如下:

  • 第一个a取得控制权,匹配正则中的a,匹配成功,控制权交给a*
  • a*取得控制权后,由于这是贪婪模式下的标识符,因此在可匹配可不匹配的情况下会优先匹配,因此尝试匹配1处的字符a;
  • 依次成功匹配a、a、a、a、a、a,接下来依旧保持贪婪特性匹配字符串中b,匹配失败,向前查找可供回溯的状态,将控制权交给a*b
  • a*b取得控制权后,开始匹配末尾处的b,匹配成功;
  • 至此,整个正则表达式匹配完毕,匹配结果为aaaaaaab,匹配过程中回溯了1次。

💡该非贪婪匹配表达式在引擎中的回溯表现如下:

  • 第一个a取得控制权,匹配正则中的a,匹配成功,控制权交给a*?
  • a*?取得控制权后,由于这是非贪婪模式下的标识符,因此在可匹配可不匹配的情况下会优先不匹配,因此尝试不匹配任何内容,将控制权交给b;
  • b取得控制权后,开始匹配1处的a,匹配失败,向前查找可供回溯的状态,控制权交给a*?,a*?吃进一个字符,index到了2处,再把控制权交给b;
  • b取得控制权后,开始匹配2处的b,匹配失败,重复上述的回溯过程,直到a*?匹配了6处的a字符,再将控制权交给b;
  • b取得控制权后,开始匹配7处的b,匹配成功;
  • 至此,整个正则表达式匹配完毕,匹配结果为aaaaaaab,匹配过程中回溯了6次。

多分支表达式匹配

1
2
'It will be confirmed in a subsequent console message'.match(/message|in|console/);
// ["in", index: 21, input: "It will be confirmed in a subsequent console message", groups: undefined] ;

💡在本例中,匹配过程如下:

  • 正则多选分支/message|in|console/从左边开始匹配,第一个message取得控制权,匹配字符串中的I,匹配失败,返回检查点进行回溯,控制权交给in;
  • in取得控制权后,匹配字符串中的I,匹配成功,在1处匹配失败,进行回溯,控制权交给 console;
  • 同上,console匹配失败,则在待匹配字符串中后移一位,即1处字符n重新进行第一步匹配;
  • 同上,所有分支均匹配失败;
  • 由待匹配字符串和上述匹配规则可知,in将在字符串n处最早匹配成功,至此,整个正则表达式匹配完毕,匹配结果为in