15-懒惰匹配,贪婪匹配
17.13 懒惰匹配,贪婪匹配
区分正则表达式专业开发者和业余爱好者的标志是对懒惰匹配和贪婪匹配的理解。正则表达式默认是贪婪的,这意味着它们会在停止运行前竭尽所能地去匹配。来看一个经典的例子。
有一些HTML文本,想将其中的标签替换成 <strong>
标签,下面是第一次尝试:
const input = "Regex pros know the difference between\n" +
"<i>greedy</i> and <i>lazy</i> matching.";
input.replace(/<i>(.*)<\/i>/ig, '<strong>$1</strong>');
替换字符串中的$1会被正则表达式(稍后详述)中的分组(.*)内容替换掉。如果运行这段代码,就会看到让人失望的结果:
"Regex pros know the difference between
<strong>greedy</i> and <i>lazy</strong> matching."
为了搞清楚这是怎么回事,就得回忆一下正则表达式引擎的工作方式,它只有在找到符合要求的匹配后,才会消费输入并且继续运行。默认情况下,它是通过贪婪模式来实现的,它会找到第一个然后说,“在找到并且确定在这个之后不存在同样的,查找是不会停止的。”因为这里有两个,所以正则会匹配到第二个,而非第一个。
有很多方式可以解决这个问题,但因为是在讨论贪婪匹配和懒惰匹配,所以使用重复元字符(*)将其转换成懒惰匹配来解决它。在后面添加一个问号即可:
input.replace(/<i>(.*?)<\/i>/ig, '<strong>$1</strong>');
与之前的相比,这个正则表达式除了在*后面添加了一个问号,其他完全一样。现在,正则表达式引擎会这样来思考:“一旦我看到一个,查找就会停止。“所以每当碰到时,它就会懒惰地停止匹配,而非继续扫描后面的内容。虽然懒惰这个词一般是负面的,但此时,懒惰正是想要的行为。
所有的重复元字符: *
、 +
、 ?
、 {n}
、 {n,}
和 {n,m}
都可以在其后跟随一个问号将它变成懒惰的(虽然在实践中,通常只把它和*、+一起使用过)。