Java教程是针对JDK 8编写的。本页中描述的示例和实践不利用后续版本引入的改进,并可能使用不再可用的技术。
请参阅Java语言更改,了解Java SE 9及后续版本中更新的语言功能的摘要。
请参阅JDK发布说明,了解所有JDK版本的新功能、增强功能以及已删除或弃用选项的信息。
X?
X??
X?+
X
贪婪 | 勉强 | 占有性 | 含义 |
---|---|---|---|
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"后面跟着?
、*
或+
。我们来看看当这些表达式与空输入字符串""
进行匹配时会发生什么:
输入你的正则表达式:a? 输入要搜索的字符串: 我找到了文本"",从索引0开始,到索引0结束。 输入你的正则表达式:a* 输入要搜索的字符串: 我找到了文本"",从索引0开始,到索引0结束。 输入你的正则表达式:a+ 输入要搜索的字符串: 未找到匹配项。
在上面的示例中,第一和第二个情况下匹配成功,因为表达式a?
和a*
都允许字母a
出现零次。您还会注意到,起始和结束索引都是零,这与我们之前见到的任何示例都不同。空输入字符串""
没有长度,因此测试仅仅在索引0处匹配空字符串。这种类型的匹配称为零长度匹配。零长度匹配可以出现在多种情况下:在空输入字符串中,在输入字符串的开头,在输入字符串的最后一个字符之后,或者在输入字符串的任意两个字符之间。零长度匹配很容易识别,因为它们始终在相同的索引位置开始和结束。
让我们通过几个例子来探索零长度匹配。将输入字符串更改为单个字母"a",你会注意到一些有趣的事情:
输入您的正则表达式:a? 输入要搜索的字符串:a 我在索引0处找到文本"a",在索引1处结束。 我在索引1处找到文本"",在索引1处结束。 输入您的正则表达式:a* 输入要搜索的字符串:a 我在索引0处找到文本"a",在索引1处结束。 我在索引1处找到文本"",在索引1处结束。 输入您的正则表达式:a+ 输入要搜索的字符串:a 我在索引0处找到文本"a",在索引1处结束。
所有三个量词都找到了字母"a",但前两个还在索引1处找到了一个零长度的匹配;也就是说,在输入字符串的最后一个字符之后。请记住,匹配器将字符"a"视为位于索引0和索引1之间的单元格,并且我们的测试工具循环,直到无法找到匹配为止。根据使用的量词,最后一个字符之后的索引处的"nothing"的存在可能会触发匹配,也可能不会触发匹配。
现在将输入字符串更改为连续五个字母"a",你会得到以下结果:
输入您的正则表达式:a? 输入要搜索的字符串:aaaaa 我在索引0处找到文本"a",在索引1处结束。 我在索引1处找到文本"a",在索引2处结束。 我在索引2处找到文本"a",在索引3处结束。 我在索引3处找到文本"a",在索引4处结束。 我在索引4处找到文本"a",在索引5处结束。 我在索引5处找到文本"",在索引5处结束。 输入您的正则表达式:a* 输入要搜索的字符串:aaaaa 我在索引0处找到文本"aaaaa",在索引5处结束。 我在索引5处找到文本"",在索引5处结束。 输入您的正则表达式:a+ 输入要搜索的字符串:aaaaa 我在索引0处找到文本"aaaaa",在索引5处结束。
表达式a?
对每个字符都找到一个匹配,因为它在出现"a"零次或一次时匹配。表达式a*
找到了两个单独的匹配:第一个匹配中的所有字母"a",然后是索引5处的最后一个字符之后的零长度匹配。最后,a+
匹配所有出现的字母"a",忽略最后一个索引处的"nothing"的存在。
此时,你可能想知道如果前两个量词遇到字母"a"之外的字母会得到什么结果。例如,如果它遇到字母"b",如"ababaaaab",会发生什么?
让我们来看看:
输入您的正则表达式:a? 输入要搜索的输入字符串:ababaaaab 我找到了文本“a”,从索引0开始到索引1结束。 我找到了文本“”,从索引1开始到索引1结束。 我找到了文本“a”,从索引2开始到索引3结束。 我找到了文本“”,从索引3开始到索引3结束。 我找到了文本“a”,从索引4开始到索引5结束。 我找到了文本“a”,从索引5开始到索引6结束。 我找到了文本“a”,从索引6开始到索引7结束。 我找到了文本“a”,从索引7开始到索引8结束。 我找到了文本“”,从索引8开始到索引8结束。 我找到了文本“”,从索引9开始到索引9结束。 输入您的正则表达式:a* 输入要搜索的输入字符串:ababaaaab 我找到了文本“a”,从索引0开始到索引1结束。 我找到了文本“”,从索引1开始到索引1结束。 我找到了文本“a”,从索引2开始到索引3结束。 我找到了文本“”,从索引3开始到索引3结束。 我找到了文本“aaaa”,从索引4开始到索引8结束。 我找到了文本“”,从索引8开始到索引8结束。 我找到了文本“”,从索引9开始到索引9结束。 输入您的正则表达式:a+ 输入要搜索的输入字符串:ababaaaab 我找到了文本“a”,从索引0开始到索引1结束。 我找到了文本“a”,从索引2开始到索引3结束。 我找到了文本“aaaa”,从索引4开始到索引8结束。
尽管字母“b”出现在单元格1、3和8中,但输出报告在这些位置上显示零长度匹配。正则表达式a?
并不是在特别寻找字母“b”,它只是寻找字母“a”的存在(或不存在)。如果量词允许“a”匹配零次,那么在输入字符串中任何不是“a”的东西都会显示为零长度匹配。剩下的“a”根据前面讨论的规则进行匹配。
要精确匹配一个模式出现的n次,只需在一对花括号中指定次数:
输入您的正则表达式:a{3} 输入要搜索的输入字符串:aa 未找到匹配。 输入您的正则表达式:a{3} 输入要搜索的输入字符串:aaa 我找到了文本“aaa”,从索引0开始到索引3结束。 输入您的正则表达式:a{3} 输入要搜索的输入字符串:aaaa 我找到了文本“aaa”,从索引0开始到索引3结束。
在这里,正则表达式a{3}
在连续三个字母“a”中进行搜索。第一个测试失败,因为输入字符串中没有足够的“a”来进行匹配。第二个测试中,输入字符串中恰好有3个“a”,触发了匹配。第三个测试也触发了匹配,因为输入字符串开头恰好有3个“a”。在那之后的任何内容对第一个匹配都不相关。如果模式在那之后再次出现,将触发后续的匹配:
输入你的正则表达式: a{3} 输入要搜索的字符串: aaaaaaaaa 我找到了文本 "aaa",起始索引为0,结束索引为3。 我找到了文本 "aaa",起始索引为3,结束索引为6。 我找到了文本 "aaa",起始索引为6,结束索引为9。
要求模式至少出现n次,只需在数字后添加逗号:
输入你的正则表达式: a{3,} 输入要搜索的字符串: aaaaaaaaa 我找到了文本 "aaaaaaaaa",起始索引为0,结束索引为9。
在相同的输入字符串中,这个测试只找到了一个匹配,因为连续的9个a满足了至少需要3个a的条件。
最后,要指定出现次数的上限,在括号内添加第二个数字:
输入你的正则表达式: a{3,6} // 查找至少连续3个(但不超过6个)a 输入要搜索的字符串: aaaaaaaaa 我找到了文本 "aaaaaa",起始索引为0,结束索引为6。 我找到了文本 "aaa",起始索引为6,结束索引为9。
在这里,第一个匹配被强制停止在上限为6个字符的位置。第二个匹配包含剩下的字符,恰好是三个a - 这是此匹配允许的最少字符数。如果输入字符串短一个字符,就不会有第二个匹配,因为只剩下两个a。
到目前为止,我们只测试了包含一个字符的输入字符串的量词。实际上,量词只能附加到一个字符上,所以正则表达式 "abc+" 表示的是 "a,后跟一个或多个b,后跟一个c"。它不表示 "abc" 出现一次或多次。然而,量词也可以附加到字符类和捕获组上,例如 [abc]+
(一个或多个a或b或c)或 (abc)+
(组"abc",一次或多次)。
让我们通过连续指定组(dog)
三次来进行说明。
输入你的正则表达式: (dog){3} 输入要搜索的字符串: dogdogdogdogdogdog 我找到了文本 "dogdogdog",起始索引为0,结束索引为9。 我找到了文本 "dogdogdog",起始索引为9,结束索引为18。 输入你的正则表达式: dog{3} 输入要搜索的字符串: dogdogdogdogdogdog 未找到匹配项。
在第一个示例中,找到了三个匹配,因为量词应用于整个捕获组。然而,如果去掉括号,匹配将失败,因为量词 {3}
现在只应用于字母 "g"。
类似地,我们可以将量词应用于整个字符类:
输入你的正则表达式: [abc]{3} 输入要搜索的字符串: abccabaaaccbbbc 我找到了文本 "abc",起始索引为0,结束索引为3。 我找到了文本 "cab",起始索引为3,结束索引为6。 我找到了文本 "aaa",起始索引为6,结束索引为9。 我找到了文本 "ccb",起始索引为9,结束索引为12。 我找到了文本 "bbc",起始索引为12,结束索引为15。 输入你的正则表达式: abc{3} 输入要搜索的字符串: abccabaaaccbbbc 未找到匹配项。
在第一个示例中,量词{3}
应用于整个字符类,而在第二个示例中仅应用于字母"c"。
贪婪量词被称为"贪婪",因为它们强制匹配器在尝试第一次匹配之前读取或"吃掉"整个输入字符串。如果第一次匹配尝试(整个输入字符串)失败,匹配器将从输入字符串中退一字符并重新尝试,重复此过程直到找到匹配项或没有更多字符可以退。根据表达式中使用的量词,它将尝试匹配1个或0个字符。
然而,懒惰量词采取相反的方法:它们从输入字符串的开头开始,然后每次都不情愿地吃掉一个字符以寻找匹配项。它们尝试的最后一件事是整个输入字符串。
最后,占有性量词总是吃掉整个输入字符串,仅尝试一次匹配。与贪婪量词不同,占有性量词永远不会退缩,即使这样做可以使整体匹配成功。
为了说明这一点,考虑输入字符串xfooxxxxxxfoo
。
输入你的正则表达式:.*foo // 贪婪量词 输入要搜索的字符串:xfooxxxxxxfoo 我找到了文本"xfooxxxxxxfoo",从索引0开始,到索引13结束。 输入你的正则表达式:.*?foo // 懒惰量词 输入要搜索的字符串:xfooxxxxxxfoo 我找到了文本"xfoo",从索引0开始,到索引4结束。 我找到了文本"xxxxxxfoo",从索引4开始,到索引13结束。 输入你的正则表达式:.*+foo // 占有性量词 输入要搜索的字符串:xfooxxxxxxfoo 未找到匹配项。
第一个示例使用贪婪量词.*
来查找"任意",零次或多次出现,后跟字母"f" "o" "o"
。由于量词是贪婪的,表达式的.*
部分首先吃掉整个输入字符串。此时,整体表达式无法成功,因为最后三个字母("f" "o" "o"
)已被消耗。因此,匹配器慢慢地每次退一字符,直到最右边的"foo"被排出,此时匹配成功并结束搜索。
然而,第二个示例是懒惰的,所以它首先吃掉"nothing"。因为"foo"不出现在字符串的开头,它被迫吞下第一个字母(一个"x"),这会触发第一个匹配项在0和4处。我们的测试工具继续这个过程直到输入字符串用尽。它在4和13处找到另一个匹配项。
第三个示例无法找到匹配项,因为量词是占有性的。在这种情况下,整个输入字符串被.*+
吃掉,没有剩下的部分来满足表达式末尾的"foo"。在希望一次性获取所有内容而不退缩的情况下,请使用占有性量词;在匹配不立即找到的情况下,它将优于等效的贪婪量词。