247.md 12.7 KB
Newer Older
W
init  
wizardforcel 已提交
1 2 3 4
# 量词

> 原文: [https://docs.oracle.com/javase/tutorial/essential/regex/quant.html](https://docs.oracle.com/javase/tutorial/essential/regex/quant.html)

W
wizardforcel 已提交
5
_ 量词*允许您指定要匹配的出现次数。为方便起见,下面介绍了描述贪婪,不情愿和占有量词的 Pattern API 规范的三个部分。乍一看,似乎量词`X?``X??``X?+`完全相同,因为它们都承诺匹配“`X`,一次或根本不匹配”。有一些细微的实现差异,将在本节末尾附近解释。
W
init  
wizardforcel 已提交
6 7 8 9 10 11 12 13 14 15 16 17

| 贪婪 | 不情愿的 | 所有格 | 含义 |
| --- | --- | --- | --- |
| `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”后跟`?``*``+`。让我们看看当这些表达式针对空输入字符串`""`进行测试时会发生什么:

W
wizardforcel 已提交
18
```java
W
init  
wizardforcel 已提交
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

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.

```

## 零长度匹配

W
wizardforcel 已提交
36
在上面的示例中,匹配在前两种情况下成功,因为表达式`a?``a*`都允许字母`a`出现零次。您还会注意到开始和结束索引都是零,这与我们到目前为止看到的任何示例都不同。空输入字符串`""`没有长度,因此测试只匹配索引 0 处的任何内容。此类匹配称为*零长度匹配*。在以下几种情况下可能发生零长度匹配:在空输入字符串中,在输入字符串的开头,在输入字符串的最后一个字符之后,或在输入字符串的任意两个字符之间。零长度匹配很容易识别,因为它们始终在相同的索引位置开始和结束。
W
init  
wizardforcel 已提交
37 38 39

让我们通过几个例子来探索零长度匹配。将输入字符串更改为单个字母“a”,您会发现一些有趣的内容:

W
wizardforcel 已提交
40
```java
W
init  
wizardforcel 已提交
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61

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 处找到了零长度匹配;也就是说,在输入字符串的最后一个字符之后。请记住,匹配器将字符“a”视为位于索引 0 和索引 1 之间的单元格中,并且我们的测试工具循环直到它无法再找到匹配项。根据使用的量词,最后一个字符后索引处“无”的存在可能触发也可能不触发匹配。

现在连续五次将输入字符串更改为字母“a”,您将获得以下内容:

W
wizardforcel 已提交
62
```java
W
init  
wizardforcel 已提交
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89

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”?

我们来看看:

W
wizardforcel 已提交
90
```java
W
init  
wizardforcel 已提交
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126

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_ 次数,只需指定一组括号内的数字:

W
wizardforcel 已提交
127
```java
W
init  
wizardforcel 已提交
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144

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。接下来的任何事情都与第一场比赛无关。如果该模式应该在该点之后再次出现,则会触发后续匹配:

W
wizardforcel 已提交
145
```java
W
init  
wizardforcel 已提交
146 147 148 149 150 151 152 153 154 155 156

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_ 次,请在数字后面添加逗号:

W
wizardforcel 已提交
157
```java
W
init  
wizardforcel 已提交
158 159 160 161 162 163 164 165 166 167 168

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 的需要。

最后,要指定出现次数的上限,请在大括号内添加第二个数字:

W
wizardforcel 已提交
169
```java
W
init  
wizardforcel 已提交
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185

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 个字符的上限。第二场比赛包括遗留的任何内容,恰好是三个 a - 这场比赛允许的最小字符数。如果输入字符串是一个较短的字符,则不会有第二个匹配,因为只剩下两个 a。

## 使用量词捕获组和字符类

到目前为止,我们只对包含一个字符的输入字符串测试了量词。实际上,量词只能一次附加到一个字符,因此正则表达式“abc +”表示“a,后跟 b,后跟 c 一次或多次”。这并不意味着“abc”一次或多次。但是,量词也可以附加到[字符类](char_classes.html)[捕获组](groups.html),例如`[abc]+`(a 或 b 或 c,一次或多次)或`(abc)+`(组“ abc“,一次或多次)。

让我们通过连续三次指定组`(dog)`来说明。

W
wizardforcel 已提交
186
```java
W
init  
wizardforcel 已提交
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202

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”。

同样,我们可以将量词应用于整个字符类:

W
wizardforcel 已提交
203
```java
W
init  
wizardforcel 已提交
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
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”。

## 贪婪,不情愿和占有量词之间的差异

贪婪,不情愿和占有欲量词之间存在细微差别。

W
wizardforcel 已提交
224
贪婪量词被认为是“贪婪的”,因为它们在尝试第一次匹配之前强制匹配器读入或*吃*整个输入字符串。如果第一次匹配尝试(整个输入字符串)失败,匹配器将输入字符串后退一个字符并再次尝试,重复该过程直到找到匹配项或者没有剩余字符可以退出。根据表达式中使用的量词,它将尝试匹配的最后一件事是 1 或 0 个字符。
W
init  
wizardforcel 已提交
225 226 227 228 229 230 231

然而,不情愿的量词采用相反的方法:它们从输入字符串的开头开始,然后不情愿地一次吃一个字符寻找匹配。他们尝试的最后一件事是整个输入字符串。

最后,占有量词总是吃整个输入字符串,尝试一次(并且只有一次)匹配。与贪婪的量词不同,占有量词永远不会退缩,即使这样做也会使整体匹配成功。

为了说明,考虑输入字符串`xfooxxxxxxfoo`

W
wizardforcel 已提交
232
```java
W
init  
wizardforcel 已提交
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253

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”。使用占有量词对于你想要抓住所有东西而不会退缩的情况;在没有立即找到匹配的情况下,它将胜过等效的贪心量词。