贪婪与不愿意与拥有量词
我发现了关于正则表达式的优秀教程,虽然我直观地理解了“贪婪”,“不情愿”和“占有”量词的含义,但我的理解似乎存在一个严重漏洞。
具体来说,在以下示例中:
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.
解释中提到吃整个输入字符串,信件被消耗 ,匹配釜底抽薪 ,“富”的最右边出现已经反刍等
不幸的是,尽管有很好的隐喻,但我仍然不明白被谁吃过什么......你知道另一篇教程(简洁地)解释正则表达式引擎是如何工作的吗?
或者,如果有人能够在下面的段落中有所不同的解释,那将会非常感激:
第一个例子使用贪婪的量词。*找到“任何东西”,零次或多次,后面跟着字母“f”“o”“o”。 由于量词是贪婪的,表达式的。*部分首先会吃掉整个输入字符串。 此时,由于最后三个字母(“f”“o”“o”)已被消费( 由谁? ),整体表达不能成功。 所以匹配器每次缓慢退出( 从右到左? )一个字母,直到“foo”的最右边的事件被反刍( 这是什么意思? ),此时匹配成功并且搜索结束。
然而第二个例子是不愿意的,所以它首先开始消耗( 由谁? )“没有”。 因为“foo”没有出现在字符串的开头,所以它被迫吞下( 谁吞下?)第一个字母(一个“x”),它会在0和4处触发第一个匹配。我们的测试工具会继续这个过程直到输入字符串耗尽。 它在4日和13日发现另一场比赛。
第三个例子没有找到匹配,因为量词是占有的。 在这种情况下,整个输入字符串被。* +消耗,( 如何? )在表达式末尾不留下任何东西来满足“foo”。 如果你想抓住所有的东西而没有退缩的情况下使用所有格量词( 后退是什么意思? ); 在没有立即找到匹配的情况下,它将胜过等价的贪婪量词。
我会给它一个镜头。
一个贪婪的量词首先尽可能匹配。 所以.*
匹配整个字符串。 然后匹配器会尝试匹配下面的f
,但是没有剩下的字符。 所以它会“回溯”,使得贪婪的量词匹配一个更少的东西(使字符串末尾的“o”不匹配)。 这仍然与正则表达式中的f
不匹配,所以它“回溯”了一步,使得贪婪的量词再次匹配一个更少的东西(使字符串末尾的“oo”不匹配)。 这仍然与正则表达式中的f
不匹配,所以它回溯了一步(留下字符串末尾的“foo”不匹配)。 现在,匹配器最终匹配正则表达式中的f
,并且o
和下一个o
也匹配。 成功!
不情愿的或“非贪婪的”量词首先尽可能少地匹配。 所以.*
首先不匹配,使整个字符串不匹配。 然后匹配器会尝试匹配f
后面的内容,但字符串的不匹配部分以“x”开头,所以不起作用。 所以匹配器回溯,使非贪婪量词匹配一件事(现在它匹配“x”,使“fooxxxxxxfoo”保持不匹配)。 然后它会尝试匹配成功的f
和正则表达式匹配中的o
和next o
。 成功!
在您的示例中,它会按照相同的过程,使用剩余的不匹配部分的字符串开始处理。
占有量词与贪婪量词一样,但不会回溯。 所以它开始与.*
匹配整个字符串,没有什么不匹配的。 再有就是一无所有它与匹配f
的正则表达式。 由于占有量词不会回溯,所以匹配在那里失败。
我还没有听说过'regurgitate'或'back off'的确切术语; 用来代替这些内容的短语是“回溯”,但“反刍”似乎与“回溯之前暂时被接受的内容再次丢弃”的短语一样好。
关于大多数正则表达式引擎的重要意义在于它们是回溯:他们会暂时接受潜在的部分匹配,同时尝试匹配正则表达式的全部内容。 如果正则表达式在第一次尝试时不能完全匹配,则正则表达式引擎将在其匹配项之一上回溯。 它会尝试匹配*
, +
?
,交替或{n,m}
重复,然后重试。 (是的,这个过程可能需要很长时间。)
第一个例子使用贪婪的量词。*找到“任何东西”,零次或多次,后面跟着字母“f”“o”“o”。 由于量词是贪婪的,表达式的。*部分首先会吃掉整个输入字符串。 此时,由于最后三个字母(“f”“o”“o”)已被消费( 由谁? ),整体表达不能成功。
最后三个字母, f
, o
和o
已经由最初的消耗.*
规则的一部分。 但是,正则表达式f
的下一个元素在输入字符串中没有任何内容。 该发动机将被迫走回头路其初始.*
匹配,并尝试匹配所有,但最最后一个字符。 (它可能是聪明的,会回到最后三个,因为它有三个字面意思,但我不知道这个级别的实现细节。)
所以匹配器一次一个字地缓慢退出( 从右到左 )一个字母,直到“foo”的最右边的事件被反刍( 这是什么意思? ),
这意味着foo
在匹配时暂时包含了.*
。 由于该尝试失败,因此正则表达式引擎会尝试在.*
接受少一个字符。 如果收到的是一个成功的比赛.*
在这个例子中,那么发动机可能会尝试缩短.*
匹配(从右到左,正如你所指出的,因为它是一个贪婪的资格),如果无法匹配整个输入,那么它可能会被迫重新评估它所收到的匹配.*
在我假设的例子。
指出比赛成功并且搜索结束。
然而第二个例子是不愿意的,所以它首先开始消耗( 由谁? )“没有”。 因为“富”
最初的任何内容都不会被.?*
消耗,这将消耗尽可能短的任何数量的东西,从而允许其余的正则表达式匹配。
没有出现在字符串的开头,它被迫吞下( 谁吞下?)
再次.?*
消耗的第一个字符,背弃最初的失败,以配合可能的最短匹配整个正则表达式之后。 (在这种情况下,正则表达式引擎将从.*?
到.*?
的匹配从左到右扩展,因为.*?
不情愿。)
第一个字母(一个“x”),它在0和4处触发第一个匹配。我们的测试工具继续这个过程,直到输入字符串耗尽。 它在4日和13日发现另一场比赛。
第三个例子没有找到匹配,因为量词是占有的。 在这种情况下,整个输入字符串被消耗。* +,( 如何? )
A .*+
将尽可能消耗,并且当正则表达式整体无法找到匹配时,将不会回溯以找到新的匹配项。 由于所有格形式不执行回溯,因此您可能不会看到.*+
许多用法,而是用字符类或类似限制: account: [[:digit:]]*+ phone: [[:digit:]]*+
。
这可以极大地加速正则表达式匹配,因为你告诉正则表达式引擎,如果输入不匹配,它不应该回溯潜在的匹配。 (如果你必须手工编写所有匹配的代码,这将类似于从不使用putc(3)
来“推回”一个输入字符,这与第一次尝试时可能写入的朴素代码非常相似。除了正则表达式引擎比推回的单个字符更好,它们可以将所有回退到零,然后重试。
但是,除了潜在的加速,还可以让你编写正确匹配你所需要的正则表达式。 我很难用一个简单的例子来说明:)但用占有者和贪婪的量词写一个正则表达式可以给你不同的匹配,而其中一个或另一个可能更合适。
在表达式的结尾处没有留下任何东西来满足“foo”。 如果你想抓住所有的东西而没有退缩的情况下使用所有格量词( 后退是什么意思? ); 它会跑赢大盘
在这种情况下,“后退”意味着“回溯” - 抛弃暂时的部分匹配以尝试另一次部分匹配,这可能会也可能不会成功。
在没有立即找到匹配的情况下,等价的贪婪量词。
这只是我的练习输出,
链接地址: http://www.djcxy.com/p/2155.html