量词

来源:互联网 发布:霍金预言2600年 知乎 编辑:程序博客网 时间:2024/04/29 15:54
 

量词(quantifier)允许你指定要匹配的出现次数。为了方便起见,Pattern API规范的三个部分描述greedy、reluctant和possessive量词,如表13-3所示。乍看上去,你可能认为量词X?、X??和X?+的功能完全一样,因为它们都匹配“X,一次或者完全没有”。在本节快结束时将解释它们实现的微妙区别。

表13-3  量    词

量    词

含    义

greedy

reluctant

possessive

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次

通过创建3个不同的正则表达式,我们从greedy量词开始分析:字母a后面是?、*或者+。我们来看一看把这些表达式和空白输入字符串""进行测试会发生什么情况:

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.

13.6.1  零长度匹配

在前面的例子中,前两个匹配成功,因为表达式a?和a*都允许字母a的出现次数为0。你还会注意到,开始和结束索引都为0,这和我们到目前为止见过的任何例子都不同。空白输入字符串“”没有长度,所以测试简单地和位于索引0的“无内容”匹配。这种类型的匹配被称为零长度匹配(zero-length match)。零长度匹配可能发生在这样几种情况下:在空白输入字符串中、在输入字符串的开头、在输入字符串的最后一个字符之后或者在输入字符串的任何两个字符之间。零长度匹配很容易识别,因为它们总在同一个索引位置开始和结束。

我们再通过几个例子分析零长度匹配。把输入字符串改为单一字母“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”找到了3个量词,但是前两个例子还找到了索引1位置的零长度匹配;就是在输入字符串最后会一个字符之后的位置。记住,匹配器发现字符“a”位于索引0和索引1之间的单元中,并且我们的测试示例会一直循环到再也找不到匹配为止。根据使用的量词,位于最后一个字符之后的索引位置的“无内容”可能触发匹配,也可能不触发匹配。

现在把输入字符串改为一行中有5个字母“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位置的零长度匹配。最后,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中,输出也报告了这些位置的零长度匹配。正则表达式a?并不专门搜索字母“b”;它仅仅搜索字母“a”的出现(或者不出现)。如果量词允许匹配“a”零次,那么输入字符串中不是“a”的任何内容的结果都是零长度匹配。按照前面例子中讨论的规则匹配剩余的a。

为了匹配正好出现n次的模式,只需在花括号中指定次数:

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”的3个出现。第一个测试失败,因为输入字符串中a出现的次数不够匹配要求。第二个测试的输入字符串中正好包含3个a,这就触发了匹配。第三个测试也触发匹配,因为在输入字符串的开头正好有3个a。后面跟着的任何内容都和第一个匹配不相关。如果在这个位置之后再次出现模式,就会触发后续的匹配:

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.

对于相同的输入字符串,这个测试只找到一个匹配,因为一行中的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.

这里,按照6个字符的上限强制停止第一个匹配。第二个匹配包含剩下的任何内容,正好是3个a——匹配允许的最小字符数量。如果输入字符串中少一个字符,就没有第二个匹配,因为只剩下了2个a。

13.6.2  使用量词的捕获组和字符类

到目前为止,我们只对包含一个字符的输入字符串测试了量词。实际上,一次只能把量词附加到一个字符之后,所以正则表达式abc+的含义是“a,后面是b,再后面是c出现一次或者多次”。它的含义不是“abc”一次或者多次。但是,也可以把量词附加到字符类(参见13.4节)和捕获组(见13.7节),比如[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.

第一个例子找到三个匹配,因为量词应用于整个捕获组。但是,删掉括号之后匹配失败,因为现在量词{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”。

13.6.3  greedy、reluctant和possessive量词的区别

greedy、reluctant和possessive量词之间有微妙的区别。

greedy量词被看作“贪婪的”,因为它们在试图搜索第一个匹配之前读完(或者说吃掉)整个输入字符串。如果第一个匹配尝试(整个输入字符串)失败,匹配器就会在输入字符串中后退一个字符并且再次尝试,重复这个过程,直到找到匹配或者没有更多剩下的字符可以后退为止。根据表达式中使用的量词,它最后试图匹配的内容是1个或者0个字符。

但是,reluctant量词采取相反的方式:它们从输入字符串的开头开始,然后逐步地一次读取一个字符搜索匹配。它们最后试图匹配的内容是整个输入字符串。

最后,possessive量词总是读完整个输入字符串,尝试一次(而且只有一次)匹配。和greedy量词不同,possessive从不后退,即使这样做能允许整体匹配成功。

为了演示,我们分析输入字符串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.

第一个例子使用greedy量词.*搜索“任何内容”零次或者多次,后面是字母f、o、o。因为是greedy量词,所以表达式的.*部分首先读完整个字符串。这样,整个表达式不会成功,因为最后三个字母(“f”“o”“o”)已经被消耗了。所以匹配器缓慢地一次后退一个字母,一直后退到最右侧出现“foo”为止,这里匹配成功并且搜索停止。

但是第二个例子使用的量词是reluctant量词,所以它首先消耗“无内容”。因为“foo”没有出现在字符串的开头,所以迫使它消耗掉第一个字母(x),这样就在索引0和4的位置触发第一个匹配。我们的测试示例继续处理,直到输入字符串耗尽为止。它在索引4和13找到了另一个匹配。

第三个例子找不到匹配,因为是possessive量词。这种情况下,.*+消耗整个输入字符串,在表达式的结尾没有剩下满足“foo”的内容。possessive量词用于处理所有内容,但是从不后退的情况;在没有立即发现匹配的情况下,它的性能优于功能相同的greedy量词。

原创粉丝点击