关于正则表达式:贪婪与不情愿与占有量词

Greedy vs. Reluctant vs. Possessive Quantifiers

我发现了这个关于正则表达式的优秀教程,虽然我直观地理解了"贪婪"、"不情愿"和"占有"量词的作用,但我的理解中似乎有一个严重的漏洞。

具体来说,在下面的示例中:

1
2
3
4
5
6
7
8
9
10
11
12
Enter your regex: .*foo  // greedy quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text"xfooxxxxxxfoo" starting at index 0 and ending at index 13.

Enter your regex: .*?foo  // reluctant quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text"xfoo" starting at index 0 and ending at index 4.
I found the text"xxxxxxfoo" starting at index 4 and ending at index 13.

Enter your regex: .*+foo // possessive quantifier
Enter input string to search: xfooxxxxxxfoo
No match found.

解释中提到了吃掉整个输入字符串、字母被消耗、匹配器后退、最右边出现的"foo"被反流等。

不幸的是,尽管有很好的比喻,我还是不明白谁吃了什么…你知道另一个教程(简明地)解释了正则表达式引擎是如何工作的吗?

或者,如果有人可以用不同的措辞解释以下段落,那将是非常感谢的:

The first example uses the greedy
quantifier .* to find"anything", zero
or more times, followed by the letters
"f""o""o". Because the quantifier is
greedy, the .* portion of the
expression first eats the entire input
string. At this point, the overall
expression cannot succeed, because the
last three letters ("f""o""o") have
already been consumed (by whom?). So the matcher
slowly backs off (from right-to-left?) one letter at a time
until the rightmost occurrence of
"foo" has been regurgitated (what does this mean?), at which
point the match succeeds and the
search ends.

The second example, however, is
reluctant, so it starts by first
consuming (by whom?)"nothing". Because"foo"
doesn't appear at the beginning of the
string, it's forced to swallow (who swallows?) the
first letter (an"x"), which triggers
the first match at 0 and 4. Our test
harness continues the process until
the input string is exhausted. It
finds another match at 4 and 13.

The third example fails to find a
match because the quantifier is
possessive. In this case, the entire
input string is consumed by .*+, (how?)
leaving nothing left over to satisfy
the"foo" at the end of the
expression. Use a possessive
quantifier for situations where you
want to seize all of something without
ever backing off (what does back off mean?); it will outperform
the equivalent greedy quantifier in
cases where the match is not
immediately found.


我来试试。

贪婪的量词首先尽可能匹配。所以.*匹配整个字符串。然后匹配者试图匹配后面的f,但没有留下任何字符。所以它"回溯",使得贪婪的量词少匹配一件事(字符串末尾的"o"不匹配)。这仍然与regex中的f不匹配,因此它又"回溯"了一步,使贪婪的量词再次匹配一个更少的东西(使字符串末尾的"oo"不匹配)。这仍然与regex中的f不匹配,因此它又后退了一步(使字符串末尾的"foo"不匹配)。现在,匹配者终于匹配regex中的fo和下一个o也匹配。成功!

不情愿或"非贪婪"的量词首先尽可能少地匹配。因此,.*一开始不匹配任何内容,导致整个字符串不匹配。然后matcher尝试匹配后面的f,但字符串中不匹配的部分以"x"开头,因此不起作用。所以matcher后退,使非贪婪量词匹配另一个东西(现在它匹配"x",留下"fooxxxxxfoo"不匹配)。然后它试图匹配成功的f,以及regex中的o和下一个o。成功!

在您的示例中,它随后用字符串中剩余的不匹配部分重新启动流程,并遵循相同的流程。

占有量词就像贪婪量词,但它不会回溯。所以它从.*开始匹配整个字符串,没有留下不匹配的内容。然后,在regex中,它就不需要与f匹配了。因为所有格量词没有回溯,所以匹配在那里失败。


这只是我的练习输出,使场景可视化。-

Visual Image


我以前没有听过确切的术语"后退"或"后退";将取代这些术语的短语是"后退",但"后退"似乎和"后退之前被暂时接受的内容再次扔掉"一样好。

要了解大多数regex引擎的重要一点是它们正在进行回溯:它们将暂时接受一个潜在的部分匹配,同时尝试匹配regex的整个内容。如果regex在第一次尝试时不能完全匹配,那么regex引擎将回溯其匹配项之一。它将尝试以不同的方式匹配*+?、交替或{n,m}重复,然后重试。(是的,这个过程可能需要很长时间。)

The first example uses the greedy
quantifier .* to find"anything", zero
or more times, followed by the letters
"f""o""o". Because the quantifier is
greedy, the .* portion of the
expression first eats the entire input
string. At this point, the overall
expression cannot succeed, because the
last three letters ("f""o""o") have
already been consumed (by whom?).

最后三个字母,foo已经被规则最初的.*部分消耗掉了。但是,regex中的下一个元素f在输入字符串中没有剩余内容。引擎将被迫回溯其初始.*匹配,并尝试匹配除最后一个字符以外的所有字符。(它可能很聪明,可以回溯到除最后三个之外的所有内容,因为它有三个字面术语,但我不知道这个级别的实现细节。)

So the matcher
slowly backs off (from right-to-left?) one letter at a time
until the rightmost occurrence of
"foo" has been regurgitated (what does this mean?), at which

这意味着,在匹配.*时,foo暂时包括在内。因为尝试失败,regex引擎尝试在.*中接受更少的字符。在这个例子中,如果在.*之前有一个成功的匹配,那么引擎可能会尝试缩短.*匹配(如您所指出的,从右到左,因为它是一个贪婪的限定符),如果它不能匹配整个输入,那么它可能会被迫重新评估它在edocx之前匹配的内容。1〔7〕在我的假设例子中。

point the match succeeds and the
search ends.

The second example, however, is
reluctant, so it starts by first
consuming (by whom?)"nothing". Because"foo"

最初的任何东西都不会被.?*消耗,它将消耗尽可能少的任何东西,从而允许regex的其余部分匹配。

doesn't appear at the beginning of the
string, it's forced to swallow (who swallows?) the

同样,.?*会消耗第一个字符,在对初始失败进行回溯之后,将整个regex与尽可能短的匹配进行匹配。(在这种情况下,regex引擎将对.*?的匹配从左向右扩展,因为.*?不愿意。)

first letter (an"x"), which triggers
the first match at 0 and 4. Our test
harness continues the process until
the input string is exhausted. It
finds another match at 4 and 13.

The third example fails to find a
match because the quantifier is
possessive. In this case, the entire
input string is consumed by .*+, (how?)

一个.*+将消耗尽可能多的资源,当regex作为一个整体找不到匹配时,它将不会回溯到新的匹配。因为所有格形式不执行回溯,所以您可能不会看到.*+的许多用法,而是使用字符类或类似的限制:account: [[:digit:]]*+ phone: [[:digit:]]*+

这可以极大地加速regex匹配,因为您告诉regex引擎,如果输入不匹配,它不应该在潜在匹配上回溯。(如果您必须手工编写所有匹配的代码,这将类似于从不使用putc(3)来"向后推"输入字符。它将非常类似于人们在第一次尝试时编写的幼稚代码。除了regex引擎比push back的单个字符要好得多,它们还可以将所有内容倒回零并重试。:)

但除了潜在的加速之外,这还可以让您编写完全匹配所需匹配内容的regex。我很难想出一个简单的例子:)但是用所有格量词和贪婪量词写一个正则表达式可以给你不同的匹配,其中一个可能更合适。

leaving nothing left over to satisfy
the"foo" at the end of the
expression. Use a possessive
quantifier for situations where you
want to seize all of something without
ever backing off (what does back off mean?); it will outperform

在这个上下文中,"后退"意味着"后退"——丢弃一个暂时的部分匹配来尝试另一个可能成功或不成功的部分匹配。

the equivalent greedy quantifier in
cases where the match is not
immediately found.


swtch.com http:/ / / / / regexp1.html regexp ~ .

我不知道这是最好的解释,在互联网上,但它也有详细的书面和保持下来,和我来。你可能要检查它。

如果你想要一个更高级别的(不详细说明),如一个简单的正则表达式,你看,a正则表达式引擎的作品回溯。基本上,它chooses("吃")的部分的字符串匹配正则表达式和商务"的那部分。如果它的比赛。如果需要,它的引擎选择老年节的字符串和正则表达式匹配的是商务部",等等,直到它……每一个可能的选择。

这个过程是用于在其recursively:试图匹配一个给定的正则表达式的字符串,正则表达式引擎想分裂的一块和一个单独的每个应用的算法。

之间的差别和不情愿的,贪婪的,当发动机enters possessive量词的选择什么是制作它的部分字符串匹配的尝试,对如何选择和修改,如果它不工作的第一次。规则是为如下:

  • a贪婪量词的引擎的开始与整个字符串(或至少,它是所有不匹配的部件已经被以前的正则表达式)和检查是否它匹配的正则表达式。如果如此,伟大的;漫游引擎继续与其余的CAN的正则表达式。如果需要,它试图再次,但trimming(最后一个字符的字符串)关节的检查。如果这不工作时,它与另一个字符,那么a贪婪量词等检查可能的最长最短的比赛从一阶。

  • 一个不情愿的量词告诉搜索引擎可能开始与一个字符串。如果它可以继续比赛,如果引擎;逆境,它添加一个字符串的字符部分和被检查的国家,直到它发现,在一场比赛或整个字符串已被使用。检查可能不情愿这样一阶量词从最短到长的比赛。

  • a possessive贪婪量词量词是像一个开放的第一:它的引擎开始通过检查整个字符串。的差异是,如果它不工作,possessive量词匹配失败的报告,然后和那里。在不改变发动机的部分字符串被看着,它不能做任何更多的尝试。

这就是为什么在你的possessive量词匹配失败的例子:.*+不会对整个字符串,它匹配的发动机,然后去到人物的外观额外的foo当然这之后,但它不喜欢他们,因为你已经在结束的字符串。如果它是一个贪婪的量词,它使.*回溯试试最一只匹配到负载的字符,然后是第三个字符是最新的负载,那么负载四个字符,这是因为只有有一succeeds foo左后,安切洛蒂都eaten".*在字符串的一部分。


以下是我使用单元格和索引位置的看法(请参阅此处的图表以区分单元格和索引)。
好的。

贪婪-尽可能匹配贪婪量词和整个正则表达式。如果没有匹配,则回溯贪婪量词。好的。

输入字符串:xfooxxxxxxfoo
正则表达式:*fo好的。

上述regex有两部分:
(i).*和
(ii)"foo"

下面的每个步骤都将分析这两个部分。与"pass"或"fail"匹配的附加注释在大括号中解释。< BR>好的。

第1步:
(i).*=xfoxxxxxfoo-pass(".*"是贪婪的量词,将使用整个输入字符串)
(ii)foo=索引13后没有字符可匹配-失败
匹配失败。好的。

第2步:
(i).*=xfooxxxxxfo-pass(对贪婪量词'.*'的回溯)
(ii)foo=o-失败
匹配失败。好的。

第3步:
(i).*=xfooxxxxxf-通过(对贪婪量词'.*'的回溯)
(ii)foo=oo-失败
匹配失败。好的。

第4步:
(i).*=xfooxxxxxx-通过(对贪婪量词'.*'的回溯)
(ii)foo=foo-通过
报表匹配好的。

结果:1个匹配项
我发现文本"xfooxxxxxxfoo"从索引0开始,到索引13结束。
好的。

勉强-尽可能少地匹配勉强量词,并匹配整个regex。如果没有匹配项,则向不愿意使用的量词添加字符。好的。

输入字符串:xfooxxxxxxfoo
正则表达式:*?福好的。

上述regex有两部分:
(我)"*?"
(二)"福"好的。

第1步:
*?="(空白)-通过(尽可能少地与不情愿的量词匹配)。*?".index 0 having""is a match。)
foo=xfo-失败(单元格0,1,2-即索引介于0和3之间)
匹配失败。好的。

第2步:
*?=x-通过(向不愿意使用的量词'.*?'添加字符).具有"x"的单元格0是匹配的。)
foo=foo-通过
报表匹配好的。

第3步:
*?="(空白)-通过(尽可能少地与不情愿的量词匹配)。*?".index 4 having""is a match。)
foo=xxx-失败(单元格4,5,6-即索引介于4和7之间)
匹配失败。好的。

第4步:
*?=x-通过(向不愿意使用的量词'.*?'添加字符)细胞4)
foo=xxx-失败(单元格5,6,7-即指数介于5和8之间)
匹配失败。好的。

第5步:
*?=xx-通过(将字符添加到不愿意使用的量词'.*?'.单元格4到5.)
foo=xxx-失败(单元格6、7、8-即索引介于6和9之间)
匹配失败。好的。

第6步:
*?=xxx-通过(向不愿意使用的量词'.*?'添加字符).单元格4到6.)
foo=xxx-失败(单元格7、8、9-即索引介于7和10之间)
匹配失败。好的。

第7步:
*?=XXXX-通过(向不愿意使用的量词'.*?'添加字符).单元格4到7.)
foo=xxf-失败(单元格8,9,10-即索引介于8和11之间)
匹配失败。好的。

第8步:
*?=XXXXX-通过(向不愿意使用的量词添加字符'.*?'.单元格4到8。)
foo=xfo-失败(单元格9、10、11-即索引介于9和12之间)
匹配失败。好的。

第9步:
*?=XXXXXX-通过(向不愿意使用的量词'.*?'添加字符).单元格4到9.)
foo=foo-通过(单元格10,11,12-即索引介于10和13之间)
报表匹配好的。

第10步:
*?="(空白)-通过(尽可能少地与不情愿的量词匹配)。*?".索引13为空。)
foo=no character left to match-fail(index 13之后没有要匹配的内容)
匹配失败。好的。

结果:2个匹配项
我发现文本"xfoo"从索引0开始,到索引4结束。
我发现文本"xxxx foo"从索引4开始,到索引13结束。
好的。

所有格-尽可能匹配所有格分位数和匹配整个正则表达式。不要后退。好的。

输入字符串:xfooxxxxxxfoo
正则表达式:*+fo好的。

上面的regex有两个部分:".*+"和"foo"。好的。

第1步:
.+=XFOOXXXXFOO-通过(尽可能匹配所有格量词'.*')
foo=no character left to match-fail(index 13后无需匹配)
匹配失败。好的。

注意:不允许回溯。好的。

结果:0个匹配项好的。好啊。


贪婪的量化涉及到在迭代期间使用字符串的所有剩余未验证字符进行模式匹配。未验证的字符从活动序列开始。每次不匹配时,将隔离末尾的字符并再次执行检查。

当活动序列只满足regex模式的前导条件时,将尝试根据隔离验证其余条件。如果验证成功,将验证隔离区中的匹配字符,剩余的不匹配字符将保持未验证状态,并在下次迭代中重新开始时使用。

字符流是从活动序列进入隔离区的。由此产生的行为是尽可能多的原始序列包含在匹配中。

不情愿的量化基本上与贪婪的限定相同,只是字符流是相反的——也就是说,它们从隔离区开始,然后流入活动序列。由此产生的行为是,在匹配中尽可能少地包含原始序列。

占有量化并没有隔离,而是以固定的活动序列包含所有内容。


贪婪的:"最长的可能匹配的序列的人物"

不情愿的:"可能的最短序列匹配的人物"

一位possessive:这是不奇怪的,因为它(在对比到贪婪和不情愿的)试图找到一个匹配的完整的正则表达式。

顺便说一下:没有正则表达式模式匹配的实现都使用回溯。所有的模式匹配是非常真实的,几乎独立的近似复杂的正则表达式!