`

java正则表达式(java.util.regex包)

阅读更多
1.正则表达式(regular expression)

正则表达式(regular expression)是根据字符串集合内每个字符串共享的共同特性来描述字符串集合的一种途径。正则表达式可以用于搜索、编辑或者处理文本和数据。必须了解创建正则表达式的特定语法——这超出了Java编程语言的一般语法。正则表达式的复杂性各不相同。但是了解了如何构造正则表达式的基础之后,就能够解释(或者创建)任何正则表达式。
本章讲解java.util.regex API支持的正则表达式语法,并且提供若干实例以便演示各种对象如何交互。在正则表达式的领域中,有很多形式可供选择,比如grep、Perl、Tcl、Python、PHP和awk。java.util.regex API中的正则表达式语法和Perl最为类似。


2.java.util.regex这个包如何表示正则表达式

java.util.regex包主要由三部分构成:Pattern、Matcher和PatternSyntax- Exception。
l Pattern对象是正则表达式编译后的表达形式。Pattern类没有提供公共构造器。为了创建模式,首先必须调用它的一个public static compile方法,这样会返回一个Pattern对象。这些方法接受正则表达式作为第一个实参;本章下面几页将讲解所需的语法。
l Matcher对象是解释模式和对输入字符串执行匹配操作的引擎。和Pattern类一样,Matcher没有定义公共构造器。通过调用Pattern对象的matcher方法获得Matcher对象。
l PatternSyntaxException对象是不可控异常,它指出正则表达式模式中的语法错误。
本章的最后几节将详细讲解上述每个类。但是首先必须了解如何构造正则表达式。因此,下一小节介绍测试示例,以后将重复地使用它来介绍语法。

3.测试代码

本节定义一个可重用的测试示例RegexTestHarness.java,用于讲解这个API支持的正则表达式结构。运行这段代码的命令是java RegexTestHarness;不接受命令行参数。这个应用程序重复地循环,提示用户输入正则表达式和输入字符串。使用这个测试示例是可选的,但是你会发现使用它分析后面章节讨论的测试案例是很方便的。
import java.io.Console;

import java.util.regex.Pattern;

import java.util.regex.Matcher;

public class RegexTestHarness {

  public static void main(String[] args){

    Console console = System.console();

    if (console == null) {

      System.err.println("No console.");

      System.exit(1);

    }

    while (true) {

      Pattern pattern =

      Pattern.compile(console.readLine("%nEnter your " +

                                                   regex: "));

      Matcher matcher =

      pattern.matcher(console.readLine("Enter input string " +

                                                to search: "));

      boolean found = false;

      while (matcher.find()) {

        console.format("I found the text /"%s/" starting " +

                               "at index %d and ending at " +

                               "index %d.%n", matcher.group(),

                               matcher.start(), matcher.end());

        found = true;

      }

      if(!found){

        console.format("No match found.%n");

      }

    }

  }

}



4.字符串面量

这个API提供的模式匹配的最基本形式是字符串字面量的匹配。例如,如果正则表达式为foo,输入字符串为foo,那么匹配结果就成功,因为两个字符串相等。试一试下面的测试示例:

Enter your regex: foo

Enter input string to search: foo

I found the text "foo" starting at index 0 and ending at index 3.

这个匹配成功。注意,当输入字符串包含3个字符时,开始索引为0,结束索引为3。按照约定,范围包含开始索引,而不包含结束索引,如图13-1所示。

o


o


f


索引 2


索引 1


单元 2


单元 1


索引 0


单元 0


图13-1  String字面量“foo”,标出了编号的单元和索引值

字符串中的每个字符都在自己的单元(cell)中,索引位置指向每个单元之间。字符串“foo”从索引0开始,到索引3结束,尽管字符本身只占用单元0、1和2。

在后续的匹配中,你会注意到一些重叠;下一个匹配的开始索引和前一个匹配的结束索引相同:

Enter your regex: foo

Enter input string to search: foofoofoo

I found the text "foo" starting at index 0 and ending at index 3.

I found the text "foo" starting at index 3 and ending at index 6.

I found the text "foo" starting at index 6 and ending at index 9.

元字符

这个API还支持很多影响匹配模式的方式的特殊字符。把正则表达式改为cat.,输入字符串改为cats。输出如下:

Enter your regex: cat.

Enter input string to search: cats

I found the text "cats" starting at index 0 and ending at index 4.

这个匹配仍然成功,尽管点号(.)没有出现在输入字符串中。匹配成功的原因在于点号是元字符(metacharacter)——被匹配器解释为具有特殊含义的字符。元字符“.”的含义是“任何一个字符”,这就是这个例子中匹配成功的原因。

这个API支持的元字符有:( [ { / ^ - $ | } ] ) ? * +.。

注意 在某些情况下,前面列出的特殊字符不被当作元字符对待。随着你更多地学习如何构造正则表达式,就会遇到这种情况。但是,你可以使用这个清单检查一个特殊字符是否被当作元字符。例如,字符!、@和#永远都不会具有特殊含义。

有两种方式可以强制元字符作为普通字符:

l 在元字符前面加上反斜线,或者

l 用/Q(开始引用)和/E(结束引用)把元字符括起来。

使用这种技术时,/Q和/E可以放在表达式内的任何位置,前提是/Q先出现。


5.字符类

如果浏览Pattern类规范会看到总结支持正则表达式结构的表。表13-1描述字符类。

左边一列指定正则表达式结构,右边一列描述每个结构在什么情况下匹配。

字符类
[abc] a、b 或 c(简单类)
[^abc] 任何字符,除了 a、b 或 c(否定)
[a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围)
[a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](并集)
[a-z&&[def]] d、e 或 f(交集)
[a-z&&[^bc]] a 到 z,除了 b 和 c:[ad-z](减去)
[a-z&&[^m-p]] a 到 z,而非 m 到 p:[a-lq-z](减去)

注意 短语“字符类”中“类”这个词不表示.class文件。在正则表达式的上下文表述中,字符类是括在方括号内的字符集合。它表示这些字符将和给定输入字符串内的单一字符成功匹配。

简单类

字符类最基本的形式是方括号中简单并排放置的字符集合。例如,正则表达式[bcr]at将和单词“bat”、“cat”或者“rat”匹配,因为它定义一个字符类(接受“b”、“c”或者“r”)作为其第一个字符:

Enter your regex: [bcr]at

Enter input string to search: bat

I found the text "bat" starting at index 0 and ending at index 3.

Enter your regex: [bcr]at

Enter input string to search: cat

I found the text "cat" starting at index 0 and ending at index 3.

Enter your regex: [bcr]at

Enter input string to search: rat

I found the text "rat" starting at index 0 and ending at index 3.

Enter your regex: [bcr]at

Enter input string to search: hat

No match found.

在上面的例子中,只有当第一个字符和字符类定义的字符之一匹配时,整体匹配才成功。

a. 非

为了匹配列出的字符之外的所有字符,需要在字符类的开头插入“^”。这种技术被称为非(negation):

Enter your regex: [^bcr]at

Enter input string to search: bat

No match found.

Enter your regex: [^bcr]at

Enter input string to search: cat

No match found.

Enter your regex: [^bcr]at

Enter input string to search: rat

No match found.

Enter your regex: [^bcr]at

Enter input string to search: hat

I found the text "hat" starting at index 0 and ending at index 3.

只有当输入字符串的第一个字符不包含字符类中定义的任何字符时,匹配才成功。

b. 范围

有时候,你会希望定义一个字符类包含一个范围内的值,比如字母“a”到“h”或者数字“1”到“5”。为了指定范围,只需在要匹配的第一个和最后一个字符之间插入“-”即可,比如[1-5]或者[a-h]。也可以在类中连着放置不同范围,以便进一步扩展匹配的可能性。例如[a-zA-Z]将匹配字母表中的任何字母:a到z(小写)或者A到Z(大写)。

下面是范围和非的一些例子:

Enter your regex: [a-c]

Enter input string to search: a

I found the text "a" starting at index 0 and ending at index 1.

Enter your regex: [a-c]

Enter input string to search: b

I found the text "b" starting at index 0 and ending at index 1.

Enter your regex: [a-c]

Enter input string to search: c

I found the text "c" starting at index 0 and ending at index 1.

Enter your regex: [a-c]

Enter input string to search: d

No match found.

Enter your regex: foo[1-5]

Enter input string to search: foo1

I found the text "foo1" starting at index 0 and ending at index 4.

Enter your regex: foo[1-5]

Enter input string to search: foo5

I found the text "foo5" starting at index 0 and ending at index 4.

Enter your regex: foo[1-5]

Enter input string to search: foo6

No match found.

Enter your regex: foo[^1-5]

Enter input string to search: foo1

No match found.

Enter your regex: foo[^1-5]

Enter input string to search: foo6

I found the text "foo6" starting at index 0 and ending at index 4.

c. 并

也可以使用并(union)创建由两个或者多个独立字符类构成的单一字符类。为了创建并,只需在一个类中嵌套另一个类,比如[0-4[6-8]]。这个并创建的单一字符类匹配数字0、1、2、3、4、6、7和8。

Enter your regex: [0-4[6-8]]

Enter input string to search: 0

I found the text "0" starting at index 0 and ending at index 1.

Enter your regex: [0-4[6-8]]

Enter input string to search: 5

No match found.

Enter your regex: [0-4[6-8]]

Enter input string to search: 6

I found the text "6" starting at index 0 and ending at index 1.

Enter your regex: [0-4[6-8]]

Enter input string to search: 8

I found the text "8" starting at index 0 and ending at index 1.



Enter your regex: [0-4[6-8]]

Enter input string to search: 9

No match found.

d. 交

为了创建只和其所有嵌套类共有的字符匹配的单一字符类,需要使用&&,比如[0-9&&[345]]。这个交创建只和两个字符类共有的数字(3、4和5)匹配的单一字符类:

Enter your regex: [0-9&&[345]]

Enter input string to search: 3

I found the text "3" starting at index 0 and ending at index 1.

Enter your regex: [0-9&&[345]]

Enter input string to search: 4

I found the text "4" starting at index 0 and ending at index 1.

Enter your regex: [0-9&&[345]]

Enter input string to search: 5

I found the text "5" starting at index 0 and ending at index 1.

Enter your regex: [0-9&&[345]]

Enter input string to search: 2

No match found.

Enter your regex: [0-9&&[345]]

Enter input string to search: 6

No match found.

下面的例子显示两个范围的交:

Enter your regex: [2-8&&[4-6]]

Enter input string to search: 3

No match found.

Enter your regex: [2-8&&[4-6]]

Enter input string to search: 4

I found the text "4" starting at index 0 and ending at index 1.

Enter your regex: [2-8&&[4-6]]

Enter input string to search: 5

I found the text "5" starting at index 0 and ending at index 1.

Enter your regex: [2-8&&[4-6]]

Enter input string to search: 6

I found the text "6" starting at index 0 and ending at index 1.

Enter your regex: [2-8&&[4-6]]

Enter input string to search: 7

No match found.

e. 减

最后,可以使用减(subtraction)去掉一个或者多个嵌套字符类,比如[0-9&&[^345]]。这个例子创建从0到9的所有值,但除3、4和5之外的单一字符类:

Enter your regex: [0-9&&[^345]]

Enter input string to search: 2

I found the text "2" starting at index 0 and ending at index 1.

Enter your regex: [0-9&&[^345]]

Enter input string to search: 3

No match found.

Enter your regex: [0-9&&[^345]]

Enter input string to search: 4

No match found.

Enter your regex: [0-9&&[^345]]

Enter input string to search: 5

No match found.

Enter your regex: [0-9&&[^345]]

Enter input string to search: 6

I found the text "6" starting at index 0 and ending at index 1.

Enter your regex: [0-9&&[^345]]

Enter input string to search: 9

I found the text "9" starting at index 0 and ending at index 1.

现在我们介绍了如何创建字符类,在阅读下一小节之前你可能希望回顾一下表13-1。


6.预定义字符类

Pattern API包含很多有用的预定义字符类(predefined character class),它们提供常用正则表达式便利的简写方式。

在表13-2中,左边一列的每个结构是右边一列的字符类的简写方式。例如,/d表示数字范围(0-9),而/w表示单词字符(任何小写字母、任何大写字母、下划线或者任何数字)。应该尽可能使用预定义类。它们使你的代码更容易阅读,并且排除易混淆的字符类造成的错误。

预定义字符类
. 任何字符(与行结束符可能匹配也可能不匹配)
\d 数字:[0-9]
\D 非数字: [^0-9]
\s 空白字符:[ \t\n\x0B\f\r]
\S 非空白字符:[^\s]
\w 单词字符:[a-zA-Z_0-9]
\W 非单词字符:[^\w]

以反斜线开头的结构被称为转义结构(escaped construct)。我们在3.1.2节的第2小节简单介绍了转义结构,其中提到了用于引用的反斜线、/Q和/E。如果你在字符串字面量中使用转义结构,就必须在反斜线前面再加上一个反斜线,以便能够编译字符串。例如:

private final String REGEX = "//d"; // a single digit

在这个例子中,/d是正则表达式;附加的反斜线是编译代码所必须的。但是,测试示例直接从控制台读取表达式,所以附加的反斜线不是必须的。

下面的例子演示预定义字符类的使用:

Enter your regex: .

Enter input string to search: @

I found the text "@" starting at index 0 and ending at index 1.

Enter your regex: .

Enter input string to search: 1

I found the text "1" starting at index 0 and ending at index 1.

Enter your regex: .

Enter input string to search: a

I found the text "a" starting at index 0 and ending at index 1.

Enter your regex: /d

Enter input string to search: 1

I found the text "1" starting at index 0 and ending at index 1.

Enter your regex: /d

Enter input string to search: a

No match found.

Enter your regex: /D

Enter input string to search: 1

No match found.

Enter your regex: /D

Enter input string to search: a

I found the text "a" starting at index 0 and ending at index 1.

Enter your regex: /s

Enter input string to search:

I found the text " " starting at index 0 and ending at index 1.

Enter your regex: /s

Enter input string to search: a

No match found.

Enter your regex: /S

Enter input string to search:

No match found.

Enter your regex: /S

Enter input string to search: a

I found the text "a" starting at index 0 and ending at index 1.

Enter your regex: /w

Enter input string to search: a

I found the text "a" starting at index 0 and ending at index 1.

Enter your regex: /w

Enter input string to search: !

No match found.

Enter your regex: /W

Enter input string to search: a

No match found.

Enter your regex: /W

Enter input string to search: !

I found the text "!" starting at index 0 and ending at index 1.

在前三个例子中,正则表达式是简单的.(“点号”元字符表示“一个任何字符”)。因此,前三个例子都匹配成功(其中随机地使用一个@字符、一个数字和一个字母)。其余的例子分别使用表13-2中的正则表达式结构。可以参考这个表来分析每个匹配背后的逻辑:

l /d匹配所有数字。

l /s匹配空白。

l /w匹配单词字符。

另一种方式是使用大写字母表示相反含义:

l /D匹配非数字。

l /S匹配非空白。

l /W匹配非单词字符。


7.量词

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

Greedy 数量词
X? X,一次或一次也没有
X* X,零次或多次
X+ X,一次或多次
X{n} X,恰好 n 次
X{n,} X,至少 n 次
X{n,m} X,至少 n 次,但是不超过 m 次
 
Reluctant 数量词
X?? X,一次或一次也没有
X*? X,零次或多次
X+? X,一次或多次
X{n}? X,恰好 n 次
X{n,}? X,至少 n 次
X{n,m}? X,至少 n 次,但是不超过 m 次
 
Possessive 数量词
X?+ X,一次或一次也没有
X*+ X,零次或多次
X++ X,一次或多次
X{n}+ X,恰好 n 次
X{n,}+ X,至少 n 次
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量词。


8.捕获组

前面我们讲过如何把量词一次附加到一个字符、字符类或者捕获组之后。但是到目前为止,我们还没有详细讨论过捕获组的概念。

捕获组(capturing group)是把多个字符当作一个单元对待的一种方式。通过把字符括在括号内创建捕获组。例如,正则表达式(dog)创建包含字母“d”、“o”和“g”的一个组。输入字符串和捕获组匹配的那一部分将被保存在内存中,以便以后通过反向引用再次使用(见13.7.2节的讨论)。

13.7.1  编号

如Pattern API中所述,按照从左到右的顺序计算捕获组的前括号数目,给捕获组编号。例如,在表达式((A)(B(C)))中,有4个这样的组:

(1) ((A)(B(C)))

(2) (A)

(3) (B(C))

(4) (C)

为了查出表达式中有多少个组,可以对匹配器对象调用groupCount方法。groupCount方法返回一个int,表示匹配器模式中出现的捕获组的数量。在这个例子中,groupCount将返回4,显示这个模式包含4个捕获组。

还有一个特殊的组,组0,它总是代表整个表达式。这个组不包括在groupCount报告的总数内。以(?开头的组是纯粹的非捕获组(non-capturing group),它不捕获文本,也不计入组的总数。(后面的13.9节中将给出非捕获组的例子。)

了解组如何编号非常重要,因为一些Matcher方法接受指定特定捕获组编号的int作为参数:

l public int start(int group)——返回前一个匹配操作期间,给定组捕获的子序列的开始索引。

l public int end(int group)——返回前一个匹配操作期间,给定组捕获的子序列的最后一个字符的索引加1。

l public String group(int group)——返回前一个匹配操作期间,给定组捕获的输入子序列。

13.7.2  反向引用

输入字符串中和捕获组匹配的部分被保存在内存中,以便以后通过反向引用(backreference)再次使用。反向引用在正则表达式中被指定为反斜线(/),后面跟着一个数字,指出要再次使用的组的编号。例如,表达式(/d/d)定义匹配一行中两个数字的一个捕获组,以后可以通过反向引用/1再次使用它。

为了匹配任意两个数字,后面跟着完全相同的两个数字,可以使用(/d/d)/1作为正则表达式:

Enter your regex: (/d/d)/1

Enter input string to search: 1212

I found the text "1212" starting at index 0 and ending at index 4.

如果改动最后两个数字,匹配就会失败:

Enter your regex: (/d/d)/1

Enter input string to search: 1234

No match found.

对于嵌套捕获组,反向引用的工作方式完全相同:指定一个反斜线,后面跟着要再次使用的组的编号。

http://blog.csdn.net/aini_420/article/details/1769129.

分享到:
评论

相关推荐

    智能车竞赛介绍(竞赛目标和赛程安排).zip

    全国大学生智能汽车竞赛自2006年起,由教育部高等教育司委托高等学校自动化类教学指导委员会举办,旨在加强学生实践、创新能力和培养团队精神的一项创意性科技竞赛。该竞赛至今已成功举办多届,吸引了众多高校学生的积极参与,此文件为智能车竞赛介绍

    集字卡v4.3.4微信公众号原版三种UI+关键字卡控制+支持强制关注.zip

    字卡v4.3.4 原版 三种UI+关键字卡控制+支持获取用户信息+支持强制关注 集卡模块从一开始的版本到助力版本再到现在的新规则版本。 集卡模块难度主要在于 如何控制各种不同的字卡组合 被粉丝集齐的数量。 如果不控制那么一定会出现超过数量的粉丝集到指定的字卡组合,造成奖品不够的混乱,如果大奖价值高的话,超过数量的粉丝集到大奖后,就造成商家的活动费用超支了。我们冥思苦想如何才能限制集到指定字卡组合的粉丝数,后我们想到了和支付宝一样的选一张关键字卡来进行规则设置的方式来进行限制,根据奖品所需的关键字卡数,设定规则就可以控制每种奖品所需字卡组合被粉丝集到的数量,规则可以在活动进行中根据需要进行修改,活动规则灵活度高。新版的集卡规则,在此次政府发布号的活动中经受了考验,集到指定字卡组合的粉丝没有超出规则限制。有了这个规则限制后,您无需盯着活动,建好活动后就无人值守让活动进行就行了,您只需要时不时来看下蹭蹭上涨的活动数据即可。 被封? 无需担心,模块内置有防封功能,支持隐藏主域名,显示炮灰域名,保护活动安全进行。 活动准备? 只需要您有一个认证服务号即可,支持订阅号借用认证服务号来做活动。如果您

    出口设备线体程序详解:PLC通讯下的V90控制与开源FB284工艺对象实战指南,出口设备线体程序详解:PLC通讯与V90控制集成,工艺对象与FB284协同工作,开源学习V90控制技能,出口设备1200

    出口设备线体程序详解:PLC通讯下的V90控制与开源FB284工艺对象实战指南,出口设备线体程序详解:PLC通讯与V90控制集成,工艺对象与FB284协同工作,开源学习V90控制技能,出口设备1200线体程序,多个plc走通讯,内部有多个v90,采用工艺对象与fb284 共同控制,功能快全部开源,能快速学会v90的控制 ,出口设备; 1200线体程序; PLC通讯; 多个V90; 工艺对象; FB284; 功能开源; V90控制。,V90工艺控制:开源功能快,快速掌握1200线体程序与PLC通讯

    基于Arduino与DAC8031的心电信号模拟器资料:心电信号与正弦波的双重输出应用方案,Arduino与DAC8031心电信号模拟器:生成心电信号与正弦波输出功能详解,基于arduino +DAC

    基于Arduino与DAC8031的心电信号模拟器资料:心电信号与正弦波的双重输出应用方案,Arduino与DAC8031心电信号模拟器:生成心电信号与正弦波输出功能详解,基于arduino +DAC8031的心电信号模拟器资料,可输出心电信号,和正弦波 ,基于Arduino;DAC8031;心电信号模拟器;输出心电信号;正弦波输出;模拟器资料,基于Arduino与DAC8031的心电信号模拟器:输出心电与正弦波

    (参考项目)MATLAB口罩识别检测.zip

    MATLAB口罩检测的基本流程 图像采集:通过摄像头或其他图像采集设备获取包含面部的图像。 图像预处理:对采集到的图像进行灰度化、去噪、直方图均衡化等预处理操作,以提高图像质量,便于后续的人脸检测和口罩检测。 人脸检测:利用Haar特征、LBP特征等经典方法或深度学习模型(如MTCNN、FaceBoxes等)在预处理后的图像中定位人脸区域。 口罩检测:在检测到的人脸区域内,进一步分析是否佩戴口罩。这可以通过检测口罩的边缘、纹理等特征,或使用已经训练好的口罩检测模型来实现。 结果输出:将检测结果以可视化方式展示,如在图像上标注人脸和口罩区域,或输出文字提示是否佩戴口罩。

    kernel-debug-devel-3.10.0-1160.119.1.el7.x64-86.rpm.tar.gz

    1、文件内容:kernel-debug-devel-3.10.0-1160.119.1.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/kernel-debug-devel-3.10.0-1160.119.1.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、更多资源/技术支持:公众号禅静编程坊

    day02供应链管理系统-补充.zip

    该文档提供了一个关于供应链管理系统开发的详细指南,重点介绍了项目安排、技术实现和框架搭建的相关内容。 文档分为以下几个关键部分: 项目安排:主要步骤包括搭建框架(1天),基础数据模块和权限管理(4天),以及应收应付和销售管理(5天)。 供应链概念:供应链系统的核心流程是通过采购商品放入仓库,并在销售时从仓库提取商品,涉及三个主要订单:采购订单、销售订单和调拨订单。 大数据的应用:介绍了数据挖掘、ETL(数据抽取)和BI(商业智能)在供应链管理中的应用。 技术实现:讲述了DAO(数据访问对象)的重用、服务层的重用、以及前端JS的继承机制、jQuery插件开发等技术细节。 系统框架搭建:包括Maven环境的配置、Web工程的创建、持久化类和映射文件的编写,以及Spring配置文件的实现。 DAO的需求和功能:供应链管理系统的各个模块都涉及分页查询、条件查询、删除、增加、修改操作等需求。 泛型的应用:通过示例说明了在Java语言中如何使用泛型来实现模块化和可扩展性。 文档非常技术导向,适合开发人员参考,用于构建供应链管理系统的架构和功能模块。

    基于四旋翼无人机的PD控制研究 附Matlab代码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    C#与VB实现欧姆龙PLC的Fins TCP通信案例源码:调用动态链接库进行数据读写,定时器与计数器数据区的简洁读写操作示例,C#与VB实现欧姆龙PLC的Fins TCP通信案例源码:调用动态链接库进

    C#与VB实现欧姆龙PLC的Fins TCP通信案例源码:调用动态链接库进行数据读写,定时器与计数器数据区的简洁读写操作示例,C#与VB实现欧姆龙PLC的Fins TCP通信案例源码:调用动态链接库进行读写操作,涵盖定时器计数器数据区学习案例,C#欧姆龙plc Fins Tcp通信案例上位机源码,有c#和VB的Demo,c#上位机和欧姆龙plc通讯案例源码,调用动态链接库,可以实现上位机的数据连接,可以简单实现D区W区定时器计数器等数据区的读写,是一个非常好的学习案例 ,C#; 欧姆龙PLC; Fins Tcp通信; 上位机源码; 动态链接库; 数据连接; D区W区读写; 定时器计数器; 学习案例,C#实现欧姆龙PLC Fins Tcp通信上位机源码,读写数据区高效学习案例

    可调谐石墨烯超材料吸收体的FDTD仿真模拟研究报告:吸收光谱的化学势调节策略与仿真源文件解析,可调谐石墨烯超材料吸收体:化学势调节光谱的FDTD仿真模拟研究,可调谐石墨烯超材料吸收体FDTD仿真模拟

    可调谐石墨烯超材料吸收体的FDTD仿真模拟研究报告:吸收光谱的化学势调节策略与仿真源文件解析,可调谐石墨烯超材料吸收体:化学势调节光谱的FDTD仿真模拟研究,可调谐石墨烯超材料吸收体FDTD仿真模拟 【案例内容】该案例提供了一种可调谐石墨烯超材料吸收体,其吸收光谱可以通过改变施加于石墨烯的化学势来进行调节。 【案例文件】仿真源文件 ,可调谐石墨烯超材料吸收体; FDTD仿真模拟; 化学势调节; 仿真源文件,石墨烯超材料吸收体:FDTD仿真调节吸收光谱案例解析

    RBF神经网络控制仿真-第二版

    RBF神经网络控制仿真-第二版

    松下PLC与威纶通触摸屏转盘设备控制:FPWINPRO7与EBPRO智能编程与宏指令应用,松下PLC与威纶通触摸屏转盘设备控制解决方案:FPWINPRO7与EBPRO协同工作,实现多工位转盘加工与IE

    松下PLC与威纶通触摸屏转盘设备控制:FPWINPRO7与EBPRO智能编程与宏指令应用,松下PLC与威纶通触摸屏转盘设备控制解决方案:FPWINPRO7与EBPRO协同工作,实现多工位转盘加工与IEC编程模式控制,松下PLC+威纶通触摸屏的转盘设备 松下PLC工程使用程序版本为FPWINPRO7 7.6.0.0版本 威纶通HMI工程使用程序版本为EBPRO 6.07.02.410S 1.多工位转盘加工控制。 2.国际标准IEC编程模式。 3.触摸屏宏指令应用控制。 ,松下PLC; 威纶通触摸屏; 转盘设备控制; 多工位加工控制; IEC编程模式; 触摸屏宏指令应用,松下PLC与威纶通HMI联控的转盘设备控制程序解析

    基于循环神经网络(RNN)的多输入单输出预测模型(适用于时间序列预测与回归分析,需Matlab 2021及以上版本),基于循环神经网络(RNN)的多输入单输出预测模型(matlab版本2021+),真

    基于循环神经网络(RNN)的多输入单输出预测模型(适用于时间序列预测与回归分析,需Matlab 2021及以上版本),基于循环神经网络(RNN)的多输入单输出预测模型(matlab版本2021+),真实值与预测值对比,多种评价指标与线性拟合展示。,RNN预测模型做多输入单输出预测模型,直接替数据就可以用。 程序语言是matlab,需求最低版本为2021及以上。 程序可以出真实值和预测值对比图,线性拟合图,可打印多种评价指标。 PS:以下效果图为测试数据的效果图,主要目的是为了显示程序运行可以出的结果图,具体预测效果以个人的具体数据为准。 2.由于每个人的数据都是独一无二的,因此无法做到可以任何人的数据直接替就可以得到自己满意的效果。 这段程序主要是一个基于循环神经网络(RNN)的预测模型。它的应用领域可以是时间序列预测、回归分析等。下面我将对程序的运行过程进行详细解释和分析。 首先,程序开始时清空环境变量、关闭图窗、清空变量和命令行。然后,通过xlsread函数导入数据,其中'数据的输入'和'数据的输出'是两个Excel文件的文件名。 接下来,程序对数据进行归一化处理。首先使用ma

    【图像识别】手写文字识别研究 附Matlab代码+运行结果.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    旅游管理系统(基于springboot,mysql,java).zip

    旅游管理系统中的功能模块主要是实现管理员;首页、个人中心、用户管理、旅游方案管理、旅游购买管理、系统管理,用户;首页、个人中心、旅游方案管理、旅游购买管理、我的收藏管理。前台首页;首页、旅游方案、旅游资讯、个人中心、后台管理等功能。经过认真细致的研究,精心准备和规划,最后测试成功,系统可以正常使用。分析功能调整与旅游管理系统实现的实际需求相结合,讨论了Java开发旅游管理系统的使用。 从上面的描述中可以基本可以实现软件的功能: 1、开发实现旅游管理系统的整个系统程序;  2、管理员;首页、个人中心、用户管理、旅游方案管理、旅游购买管理、系统管理等。 3、用户:首页、个人中心、旅游方案管理、旅游购买管理、我的收藏管理。 4、前台首页:首页、旅游方案、旅游资讯、个人中心、后台管理等相应操作; 5、基础数据管理:实现系统基本信息的添加、修改及删除等操作,并且根据需求进行交流查看及回复相应操作。

    Boost二级升压光伏并网结构的Simulink建模与MPPT最大功率点追踪:基于功率反馈的扰动观察法调整电压方向研究,Boost二级升压光伏并网结构的Simulink建模与MPPT最大功率点追踪:基

    Boost二级升压光伏并网结构的Simulink建模与MPPT最大功率点追踪:基于功率反馈的扰动观察法调整电压方向研究,Boost二级升压光伏并网结构的Simulink建模与MPPT最大功率点追踪:基于功率反馈的扰动观察法调整电压方向研究,Boost二级升压光伏并网结构,Simulink建模,MPPT最大功率点追踪,扰动观察法采用功率反馈方式,若ΔP>0,说明电压调整的方向正确,可以继续按原方向进行“干扰”;若ΔP<0,说明电压调整的方向错误,需要对“干扰”的方向进行改变。 ,Boost升压;光伏并网结构;Simulink建模;MPPT最大功率点追踪;扰动观察法;功率反馈;电压调整方向。,光伏并网结构中Boost升压MPPT控制策略的Simulink建模与功率反馈扰动观察法

    基于matlab平台的图像去雾设计.zip

    运行GUI版本,可二开

    Deepseek相关参考资源文档

    Deepseek相关主题资源及行业影响

    WP Smush Pro3.16.12 一款专为 WordPress 网站设计的图像优化插件开心版.zip

    WP Smush Pro 是一款专为 WordPress 网站设计的图像优化插件。 一、主要作用 图像压缩 它能够在不影响图像质量的前提下,大幅度减小图像文件的大小。例如,对于一些高分辨率的产品图片或者风景照片,它可以通过先进的压缩算法,去除图像中多余的数据。通常 JPEG 格式的图像经过压缩后,文件大小可以减少 40% – 70% 左右。这对于网站性能优化非常关键,因为较小的图像文件可以加快网站的加载速度。 该插件支持多种图像格式的压缩,包括 JPEG、PNG 和 GIF。对于 PNG 图像,它可以在保留透明度等关键特性的同时,有效地减小文件尺寸。对于 GIF 图像,也能在一定程度上优化文件大小,减少动画 GIF 的加载时间。 懒加载 WP Smush Pro 实现了图像懒加载功能。懒加载是一种延迟加载图像的技术,当用户滚动页面到包含图像的位置时,图像才会加载。这样可以避免一次性加载大量图像,尤其是在页面内容较多且包含许多图像的情况下。例如,在一个新闻网站的长文章页面,带有大量配图,懒加载可以让用户在浏览文章开头部分时,不需要等待所有图片加载,从而提高页面的初始加载速度,同时也能

    1. Download this file: https://cdn-media.huggingface.co/frpc-gradio-0.3/frpc-windows-amd64.exe

    Could not create share link. Missing file: C:\Users\xx\.conda\envs\omni\Lib\site-packages\gradio\frpc_windows_amd64_v0.3 1. Download this file: https://cdn-media.huggingface.co/frpc-gradio-0.3/frpc_windows_amd64.exe 2. Rename the downloaded file to: frpc_windows_amd64_v0.3 3. Move the file to this location: C:\Users\xx\.conda\envs\omni\Lib\site-packages\gradio

Global site tag (gtag.js) - Google Analytics