Quantifiers
量词允许您指定要匹配的出现次数。为方便起见,下面介绍了 PatternAPI 规范的三个部分,分别描述了贪婪,不情愿和所有格的量词。乍一看,量词X?
,X??
和X?+
做的完全相同,因为它们都承诺匹配“ X
,一次或根本不匹配”。存在细微的实现差异,将在本节末尾进行解释。
Greedy | Reluctant | Possessive | Meaning |
---|---|---|---|
X? | X?? | X?+ | X ,一次或根本不 |
X* | X*? | X*+ | X ,零次或多次 |
X+ | X+? | X++ | X 次,一次或多次 |
X{n} | X{n}? | X{n}+ | X ,正好n 次 |
X{n,} | X{n,}? | X{n,}+ | X ,至少n 次 |
X{n,m} | X{n,m}? | X{n,m}+ | X ,至少n 但不超过m 次 |
让我们从创建三个不同的正则表达式开始看贪婪量词:字母“ a”后跟?
,*
或+
。让我们看看针对空 Importing 字符串""
测试这些表达式时会发生什么:
Enter your regex: a?
Enter input string to search:
I found the text "" starting at index 0 and ending at index 0.
Enter your regex: a*
Enter input string to search:
I found the text "" starting at index 0 and ending at index 0.
Enter your regex: a+
Enter input string to search:
No match found.
Zero-Length Matches
在上面的示例中,在前两种情况下匹配成功,因为表达式a?
和a*
都允许字母a
出现零次。您还将注意到,开始索引和结束索引均为零,这与到目前为止我们看到的任何示例都不相同。空的 Importing 字符串""
没有 Long 度,因此测试仅在索引 0 处不匹配任何内容。此类匹配称为零 Long 度匹配。零 Long 度匹配可能在几种情况下发生:在空的 Importing 字符串 中,在 Importing 字符串 的开头,在 Importing 字符串 的最后一个字符之后或在 Importing 字符串 的任何两个字符之间。零 Long 度匹配总是容易识别,因为它们始终在相同的索引位置开始和结束。
让我们通过更多示例探讨零 Long 度匹配。将 Importing 字符串 更改为单个字母“ a”,您会发现一些有趣的事情:
Enter your regex: a?
Enter input string to search: a
I found the text "a" starting at index 0 and ending at index 1.
I found the text "" starting at index 1 and ending at index 1.
Enter your regex: a*
Enter input string to search: a
I found the text "a" starting at index 0 and ending at index 1.
I found the text "" starting at index 1 and ending at index 1.
Enter your regex: a+
Enter input string to search: a
I found the text "a" starting at index 0 and ending at index 1.
所有三个量词都找到字母“ a”,但前两个量词还在索引 1 处找到零 Long 度匹配;也就是说,在 Importing 字符串 的最后一个字符之后。请记住,匹配器将字符“ a”视为位于索引 0 和索引 1 之间的单元中,并且我们的测试工具循环运行,直到无法找到匹配为止。根据所使用的量词,最后一个字符之后的索引处“无”可能会触发也可能不会触发匹配。
现在,将 Importing 字符串 连续五次更改为字母“ a”,您将获得以下内容:
Enter your regex: a?
Enter input string to search: aaaaa
I found the text "a" starting at index 0 and ending at index 1.
I found the text "a" starting at index 1 and ending at index 2.
I found the text "a" starting at index 2 and ending at index 3.
I found the text "a" starting at index 3 and ending at index 4.
I found the text "a" starting at index 4 and ending at index 5.
I found the text "" starting at index 5 and ending at index 5.
Enter your regex: a*
Enter input string to search: aaaaa
I found the text "aaaaa" starting at index 0 and ending at index 5.
I found the text "" starting at index 5 and ending at index 5.
Enter your regex: a+
Enter input string to search: aaaaa
I found the text "aaaaa" starting at index 0 and ending at index 5.
表达式a?
为每个字符找到一个单独的匹配项,因为当“ a”出现零次或一次时,它就匹配。表达式a*
找到两个单独的匹配项:第一个匹配项中的所有字母“ a”,然后在索引 5 的最后一个字符之后的零 Long 度匹配项。最后,a+
匹配所有出现的字母“ a”,忽略最后一个索引中“无”的存在。
在这一点上,您可能想知道如果前两个量词遇到的字母不是“ a”,那么结果将是什么。例如,如果遇到字母“ b”,如“ ababaaaab”,会发生什么?
让我们找出:
Enter your regex: a?
Enter input string to search: ababaaaab
I found the text "a" starting at index 0 and ending at index 1.
I found the text "" starting at index 1 and ending at index 1.
I found the text "a" starting at index 2 and ending at index 3.
I found the text "" starting at index 3 and ending at index 3.
I found the text "a" starting at index 4 and ending at index 5.
I found the text "a" starting at index 5 and ending at index 6.
I found the text "a" starting at index 6 and ending at index 7.
I found the text "a" starting at index 7 and ending at index 8.
I found the text "" starting at index 8 and ending at index 8.
I found the text "" starting at index 9 and ending at index 9.
Enter your regex: a*
Enter input string to search: ababaaaab
I found the text "a" starting at index 0 and ending at index 1.
I found the text "" starting at index 1 and ending at index 1.
I found the text "a" starting at index 2 and ending at index 3.
I found the text "" starting at index 3 and ending at index 3.
I found the text "aaaa" starting at index 4 and ending at index 8.
I found the text "" starting at index 8 and ending at index 8.
I found the text "" starting at index 9 and ending at index 9.
Enter your regex: a+
Enter input string to search: ababaaaab
I found the text "a" starting at index 0 and ending at index 1.
I found the text "a" starting at index 2 and ending at index 3.
I found the text "aaaa" starting at index 4 and ending at index 8.
即使字母“ b”出现在单元格 1、3 和 8 中,输出也会在这些位置报告零 Long 度匹配。正则表达式a?
不是专门寻找字母“ b”;而是它只是在寻找字母“ a”的存在(或缺乏)。如果量词允许“ a”匹配零次,则 Importing 字符串 中不是“ a”的任何内容都将显示为零 Long 度匹配。其余的 a 根据前面示例中讨论的规则进行匹配。
要完全匹配一个 Patternn 次,只需在一组花括号内指定数字即可:
Enter your regex: a{3}
Enter input string to search: aa
No match found.
Enter your regex: a{3}
Enter input string to search: aaa
I found the text "aaa" starting at index 0 and ending at index 3.
Enter your regex: a{3}
Enter input string to search: aaaa
I found the text "aaa" starting at index 0 and ending at index 3.
在这里,正则表达式a{3}
正在搜索连续出现的三个字母“ a”。第一次测试失败,因为 Importing 字符串 没有足够的 a 来匹配。第二个测试在 Importing 字符串 中恰好包含 3 个 a,这会触发匹配。第三个测试也会触发匹配,因为在 Importing 字符串 的开头恰好有 3 个 a。以下与第一场 match 无关的任何事情。如果该 Pattern 在该点之后再次出现,则将触发后续匹配:
Enter your regex: a{3}
Enter input string to search: aaaaaaaaa
I found the text "aaa" starting at index 0 and ending at index 3.
I found the text "aaa" starting at index 3 and ending at index 6.
I found the text "aaa" starting at index 6 and ending at index 9.
要要求图案至少出现 n 次,请在数字后添加一个逗号:
Enter your regex: a{3,}
Enter input string to search: aaaaaaaaa
I found the text "aaaaaaaaa" starting at index 0 and ending at index 9.
使用相同的 Importing 字符串,此测试仅找到一个匹配项,因为连续的 9 个 a 满足“至少” 3 个 a 的需要。
最后,要指定出现次数的上限,请在花括号内添加第二个数字:
Enter your regex: a{3,6} // find at least 3 (but no more than 6) a's in a row
Enter input string to search: aaaaaaaaa
I found the text "aaaaaa" starting at index 0 and ending at index 6.
I found the text "aaa" starting at index 6 and ending at index 9.
在这里,第一场 match 被迫停止在 6 个字符的上限。第二个匹配项包括剩下的所有内容,恰好是三个 a-该匹配项允许的最少字符数。如果 Importing 字符串 短一个字符,则不会有第二个匹配项,因为将仅保留两个 a。
使用量词catch组和字符类
到目前为止,我们仅在包含一个字符的 Importing 字符串 上测试了量词。实际上,量词一次只能附加一个字符,因此正则表达式“ abc”表示“ a,后跟 b,再后跟 c 一次或多次”。它不会一次或多次表示“ abc”。但是,量词也可以附加到Character Classes和Capturing Groups,例如[abc]+
(a 或 b 或 c,一次或多次)或(abc)+
(组“ abc”,一次或多次)。
让我们通过连续三次指定组(dog)
进行说明。
Enter your regex: (dog){3}
Enter input string to search: dogdogdogdogdogdog
I found the text "dogdogdog" starting at index 0 and ending at index 9.
I found the text "dogdogdog" starting at index 9 and ending at index 18.
Enter your regex: dog{3}
Enter input string to search: dogdogdogdogdogdog
No match found.
在这里,第一个示例找到三个匹配项,因为量词适用于整个catch组。但是,删除括号,并且匹配失败,因为量词{3}
现在仅适用于字母“ g”。
同样,我们可以对整个字符类应用量词:
Enter your regex: [abc]{3}
Enter input string to search: abccabaaaccbbbc
I found the text "abc" starting at index 0 and ending at index 3.
I found the text "cab" starting at index 3 and ending at index 6.
I found the text "aaa" starting at index 6 and ending at index 9.
I found the text "ccb" starting at index 9 and ending at index 12.
I found the text "bbc" starting at index 12 and ending at index 15.
Enter your regex: abc{3}
Enter input string to search: abccabaaaccbbbc
No match found.
在这里,量词{3}
在第一个示例中适用于整个字符类,但在第二个示例中仅适用于字母“ c”。
贪婪,勉强和所有格量词之间的差异
贪婪,勉强和所有格修饰语之间存在细微的差异。
贪婪的量词被认为是“贪婪的”,因为它们会迫使匹配器在try首次匹配之前先读取或吃掉整个 Importing 字符串。如果第一次匹配try失败(整个 Importing 字符串)失败,则匹配器将 Importing 字符串 退回一个字符,然后再次try,重复此过程,直到找到匹配项或没有其他要退回的字符。根据表达式中使用的量词,它将try与之匹配的最后一件事是 1 或 0 个字符。
但是,勉强的量词采用相反的方法:它们从 Importing 字符串 的开头开始,然后勉强地一次吃一个字符以寻找匹配项。他们try的最后一件事是整个 Importing 字符串。
最后,所有格量词总是吃掉整个 Importing 字符串,try一次(也只有一次)进行匹配。与贪婪的量词不同,所有格量词从不退缩,即使这样做会使整体匹配成功。
为了说明,请考虑 Importing 字符串xfooxxxxxxfoo
。
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"
。因为量词是贪婪的,所以表达式的.*
部分首先会吃掉整个 Importing 字符串。此时,整个表达式无法成功,因为最后三个字母("f" "o" "o"
)已被占用。因此,匹配器每次一次缓慢地退回一个字母,直到最右边出现的“ foo”重新反响为止,这时匹配成功并且搜索结束。
但是,第二个示例是勉强的,因此首先要先消耗“无”。因为“ foo”没有出现在字符串 的开头,所以它被迫吞下第一个字母(“ x”),这会在 0 和 4 处触发第一个匹配。我们的测试工具将 continue 该过程,直到 Importing 字符串 为累。它在 4 和 13 找到另一个匹配项。
第三个示例找不到匹配项,因为量词是所有格。在这种情况下,整个 Importing 字符串 将被.*+
占用,而在表达式末尾不留任何内容来满足“ foo”。如果您想抓住所有东西而又不退缩,请使用所有格量词;如果没有立即找到匹配项,它将胜过等效的贪婪量词。