理解量词
我正在通过量词的Java教程。
在贪心,不情愿和拥有量词的差异中有所不同。
我无法理解到底有什么区别。
说明如下:
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”。 使用占有量词来处理你想要抓住所有东西而又不退缩的情况; 在没有立即找到匹配的情况下,它将胜过等价的贪婪量词。
一般规则
量词的基本知识?
, *
和+
(分别为“零个或一个”,“零个或多个”,“一个或多个”)。
只有知道正则表达式解析器的工作原理时,才能理解“回溯”的含义(请参见下面的“动态示例”)。
单例解释
?
:首先测试1次出现,然后是0; 如果你发现了一次,然后你需要丢弃它,你可以做到 ??
:首先测试0个事件,然后是1 ?+
:首先测试1次,然后是0; 如果您发现了一次,然后您需要丢弃它, 则无法执行此操作 *
:尝试尽可能多地发生(甚至0); 如果您发现了N个事件,然后您需要丢弃(某些)事件,则可以从最后一次开始 *?
:尽量减少事件发生(甚至0) *+
:尝试获得尽可能多的事件(即使是0); 如果你发现了N个事件,然后你需要丢弃(一些)它们,你不能这样做 +
:尝试获得尽可能多的事件(至少1次); 如果您发现了N个事件,然后您需要丢弃(某些)事件,则可以从最后一次开始 +?
:尽可能减少事件发生(至少1次) ++
:尝试获得尽可能多的事件(至少1次); 如果你发现了N个事件,然后你需要丢弃(一些)它们,你不能这样做 动态的例子
在本节中,我将尝试向您展示正则表达式解析器背后的逻辑:
1)案例/.*foo/
:
首先是子模式/.*/
的转向。 它开始阐述第一个字符:
xfooxxxxxxfoo
^
然后它会尝试尽可能多地阐述字符:
xfooxxxxxxfoo
^^
xfooxxxxxxfoo
^^^
[...]
xfooxxxxxxfoo
^^^^^^^^^^^
xfooxxxxxxfoo
^^^^^^^^^^^^
xfooxxxxxxfoo
^^^^^^^^^^^^^
游标到达最后,但子模式/foo/
尚未发挥其作用。 所以游标“返回”允许子模式/foo/
获得匹配:
xfooxxxxxxfoo
^^^^^^^^^^^^
/foo/
仍然无法匹配,所以我们需要再次返回:
xfooxxxxxxfoo
^^^^^^^^^^^
xfooxxxxxxfoo
^^^^^^^^^^
现在子模式/foo/
可以匹配:
xfooxxxxxxfoo
^^^^^^^^^^^^^
所以匹配是整个字符串xfooxxxxxxfoo
。
2)案例/.*?foo/
:
首先是子模式/.*?/
。 这是懒惰的,所以我们希望它可以匹配0个字符。 但是如果是这样,子模式/foo/
不能匹配,所以它必须详细说明一个字符:
xfooxxxxxxfoo
^
现在是foo
的依次为:
xfooxxxxxxfoo
^^^^
所以这场比赛是xfoo
。
(如果为正则表达式设置了global
类型,那么解析器会在匹配后的第一个字符处重新开始,给出第二个匹配xxxxxxfoo
)
3)案例/.*+foo/
:
首先是子模式/.*+/
的转向。 它试图尽可能多地阐述角色:
xfooxxxxxxfoo
^
[...]
xfooxxxxxxfoo
^^^^^^^^^^^^^
游标到达最后,但子模式/foo/
尚未发挥其作用。 所以光标“回去”......哦,不,可惜,它不能(因为它是占有欲)!
所以我们没有匹配。
懒惰(不情愿)和贪婪的情况之间的主要区别在于回溯结构的行为和占有欲的行为过于激进 !
f
匹配,所以每当你有一个foo
短语时,你会得到一个匹配,这就是为什么我们从它的用法中得到多个匹配。 。*?FOO
x fooxxxxxxfoo
在开始时,懒的情况下会与x
匹配(匹配成功后),并将焦点传递给下一个运算符; foo
是正则表达式的一部分,并且因为它出现在x
之后,所以我们得到这个片段的匹配,这个字符串的次要部分是相同的。
* FOO
xfooxxxxxxfo o
当贪婪的情况下(最后一个字符)匹配将失败,因为我们无法匹配正则表达式的foo
部分。 比回溯会迫使贪婪的案件backtrace
其步骤并执行下一个运算符foo
,类似于懒惰的情况;
xfooxxxxxx f oo
此时, foo
部分将获得成功的匹配,从而以整个字符串的成功匹配结束。
backtracking
的匹配失败之外,所有格并非如此。 如果它可以匹配,它将拥有并将牺牲过程中匹配的成功。 如果它在匹配一个字符时失败了,那么只有焦点会传递给正则表达式的下一个运算符。 * + FOO
xfooxxxxxxfo o
类似贪婪的情况下,我们已经到达了字符串的末尾,但所有格仍然可以匹配它,因此不会将火炬传递给backtrack
结构,并且会导致匹配失败。