在正则表达式的上下文中,“懒惰”和“贪婪”是什么意思?

这两个术语以可理解的方式是什么?

389811 次浏览

贪婪意味着你的表达式将匹配尽可能大的组,懒惰意味着它将匹配尽可能小的组。对于这个字符串:

abcdefghijklmc

这个表达:

a.*c

贪婪匹配将匹配整个字符串,懒惰匹配将仅匹配第一个abc

贪婪表示匹配最长的字符串。

懒惰表示匹配最短的字符串。

例如,贪婪的h.+l匹配'hello'中的'hell',但懒惰的h.+?l匹配'hel'

来自正则表达式

正则中的标准量词 表达是贪婪的,意思是他们 尽可能匹配,只给予 必要时返回以匹配 正则表达式的剩余部分。

通过使用懒惰量词, 表达式尝试最小匹配 首先

贪婪会尽可能多地消耗。从http://www.regular-expressions.info/repeat.html中,我们看到了尝试将超文本标记语言标签与<.+>匹配的示例。假设您有以下内容:

<em>Hello World</em>

您可能会认为<.+>.表示任何非换行符+表示一个或多个)只会匹配<em></em>,而实际上它会非常贪婪,从第一个<到最后一个>。这意味着它将匹配<em>Hello World</em>而不是您想要的。

让它变懒(<.+?>)将防止这种情况。通过在+之后添加?,我们告诉它重复尽可能少的时间,所以它遇到的第一个>是我们想要停止匹配的地方。

我建议您下载RegExr,这是一个很好的工具,可以帮助您探索正则表达式-我一直在使用它。

图片来源:www.regular-expressions.info

贪婪:贪婪的量词首先尝试重复令牌多次 尽可能,并逐渐放弃匹配,因为引擎回溯找到 全场比赛

懒惰:懒惰量词首先根据需要重复令牌几次,然后 当引擎回溯到正则表达式时,逐渐扩展匹配 找到一个整体匹配。

贪心量词 懒惰量词 特性介绍
* *? 星形量词:0或更多
+ +? 加量词:1或更多
? ?? 可选量词:0或1
{n} {n}? 量词:正好n
{n,} {n,}? 量词:n或更多
{n,m} {n,m}? 量词:介于n和m之间

给一个量词加上一个?,使它变得不贪婪,即懒惰。

示例:
测试字符串:stackoverflow
贪心表达式s.*o输出:stackoverflow
懒reg表达式s.*?o输出:stackoverflow

尝试理解以下行为:

    var input = "0014.2";


Regex r1 = new Regex("\\d+.{0,1}\\d+");
Regex r2 = new Regex("\\d*.{0,1}\\d*");


Console.WriteLine(r1.Match(input).Value); // "0014.2"
Console.WriteLine(r2.Match(input).Value); // "0014.2"


input = " 0014.2";


Console.WriteLine(r1.Match(input).Value); // "0014.2"
Console.WriteLine(r2.Match(input).Value); // " 0014"


input = "  0014.2";


Console.WriteLine(r1.Match(input).Value); // "0014.2"
Console.WriteLine(r2.Match(input).Value); // ""

据我所知,大多数正则表达式引擎默认是贪婪的。在量词末尾添加问号将启用惰性匹配。

正如@Andre S在评论中提到的。

  • 贪婪:继续寻找,直到条件不满足。
  • 懒惰:一旦满足条件就停止搜索。

参考下面的例子,什么是贪婪,什么是懒惰。

import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class Test {
public static void main(String args[]){
String money = "100000000999";
String greedyRegex = "100(0*)";
Pattern pattern = Pattern.compile(greedyRegex);
Matcher matcher = pattern.matcher(money);
while(matcher.find()){
System.out.println("I'm greedy and I want " + matcher.group() + " dollars. This is the most I can get.");
}
        

String lazyRegex = "100(0*?)";
pattern = Pattern.compile(lazyRegex);
matcher = pattern.matcher(money);
while(matcher.find()){
System.out.println("I'm too lazy to get so much money, only " + matcher.group() + " dollars is enough for me");
}
}
}

结果是:
I'm greedy and I want 100000000 dollars. This is the most I can get.


I'm too lazy to get so much money, only 100 dollars is enough for me

贪婪匹配。正则表达式的默认行为是贪婪。这意味着它试图尽可能多地提取,直到它符合模式,即使较小的部分在语法上就足够了。

示例:

import re
text = "<body>Regex Greedy Matching Example </body>"
re.findall('<.*>', text)
#> ['<body>Regex Greedy Matching Example </body>']

它不是匹配到“>”的第一次出现,而是提取整个字符串。这是正则表达式的默认贪婪或“全部拿走”行为。

惰性匹配,另一方面,“需要尽可能少”。这可以通过在模式末尾添加?来实现。

示例:

re.findall('<.*?>', text)
#> ['<body>', '</body>']

如果您只想检索第一个匹配项,请改用搜索方法。

re.search('<.*?>', text).group()
#> '<body>'

图片来源:Python正则表达式示例

贪婪意味着它会消耗你的模式,直到它们都不剩,它再也看不见了。

Lazy将在遇到您请求的第一个模式时立即停止。

我经常遇到的一个常见例子是正则表达式的\s*-\s*?([0-9]{2}\s*-\s*?[0-9]{7})

由于*,第一个\s*被归类为贪婪,并且在遇到数字后将寻找尽可能多的空格,然后寻找破折号字符“-”。而第二个\s*?由于*?的存在而懒惰,这意味着它将查看第一个空格字符并停在那里。

最佳示例显示。字符串。192.168.1.1和贪婪的正则表达式\b.+\b 你可能认为这会给你第一个八位字节,但实际上是与整个字符串匹配的。为什么?因为.+是贪婪的,贪婪的匹配匹配192.168.1.1中的每个字符,直到它到达字符串的末尾。这是重要的一点!现在它开始一次回溯一个字符,直到找到第三个令牌(\b)的匹配。

如果字符串4GB文本文件和192.168.1.1是在开始,你可以很容易地看到这个回溯会导致一个问题。

要使正则表达式不贪婪(懒惰),请在贪婪搜索后加上问号,例如

*?
??
+?

现在发生的是令牌2(+?)找到一个匹配项,正则表达式沿着一个字符移动,然后尝试下一个令牌(\b)而不是令牌2(+?)。所以它小心翼翼地爬行。

贪婪的量词就像国税局

他们会尽可能多地拿走。例如与此正则表达式匹配:.*

$50,000

再见银行余额。

在这里看到一个例子:贪心榜样

非贪婪量词——它们尽可能少地使用

要求退税:美国国税局突然变得不贪婪-并尽可能少地返回:即他们使用这个量词:

(.{2,5}?)([0-9]*)对这个输入:$50,000

第一组不需要,只匹配$5-所以我得到了50,000美元输入的$5退款。

请参阅此处:非贪心模型

为什么我们需要贪婪与不贪婪?

如果你试图匹配表达式的某些部分,这就变得很重要。有时你不想匹配所有的东西——尽可能少。有时你想尽可能多地匹配。仅此而已。

您可以在上面发布的链接中使用示例。

(类比用来帮助你记住)。

为了进一步澄清懒惰,这里有一个例子,乍一看可能并不直观,但解释了Suganthan Madhavan Pillai回答的“逐渐扩大比赛”的想法。

input -> some.email@domain.com@
regex -> ^.*?@$

此输入的正则表达式将有一个匹配项。乍一看,有人可能会说LAZY匹配(";. *? @";)将在first@停止,之后它将检查输入字符串是否结束("$")。遵循此逻辑,有人会得出没有匹配的结论,因为输入字符串不会在first@之后结束。

但正如你所看到的,情况并非如此,正则表达式将继续前进,即使我们使用非贪婪(懒惰模式)搜索,直到它达到第二个@并具有最小匹配。