Java正则表达式教程 [1]
Regular Expressions of Java Tutorial
译者序(下载
  正则表达式长于处理文本,对匹配、查找和替换等操作都有意想不到的效果。正因如此,正则表达式现在是作为程序员七种根本技能之一*,因而学习和运用它在作业中都能到达很高的功率。
  正则表达式运用于程序设计言语中,初次是呈现在 Perl 言语,这也让 Perl 奠定了正则表达式旗手的方位。现在,它现已深化到了一切的程序设计言语中,在程序设计言语中,正则表达式能够说是规范装备了。
  Java 中从 JDK 1.4 开端添加了对正则表达式的支撑,至此正则表达式成为了 Java 中的根本类库,运用时不需求再导入第三方的类库了。Java 正则表达式的语法来源于标志着正则表达式规范的 Perl 言语,但也不是彻底相同的,详细的能够参看 Pattern 类的 API 文档阐明。
  我在一次偶尔中发现了坐落 java.sun.com 站点上的 Java Tutorial,也在那里看到了关于 Java 的正则表达式教程,感觉它不同于其他的正则表达式教程,文中以很多的匹配实例来进行阐明。为了能让 Java 学习者能更好地运用正则表达式,就将其完好地译出了。该教程中所介绍的正则表达式运用只是是最为简略的(并没有彻底地触及到 Pattern 类支撑的一切正则表达式语法,也没有触及到高档的运用),适合于从未触摸过或许是没有彻底了解正则表达式根底的学习者。在学习完该教程后,应该对正则表达式有了开端的了解,并能熟练地运用 java.util.regex 包中的关于正则表达式的类库,为往后学习更高档的正则表达式技能奠定杰出的根底。
  教程中一切的源都在 src 目录下,能够直接编译运转。因为当时版别的 Java Tutorial 是依据 JDK 6.0 的,因而其间的示例程序也用到了 JDK 6.0 中的新增类库,但正则表达式在 JDK 1.4 就现已存在了,为了便利咱们运用,改写了部分的源,源类名中后缀为“V4”的表明用于 JDK 1.4 或以上版别,“V5”的表明用于 JDK 5.0 或以上版别,没有这些后缀的类在各个版别中均能够正常运用。
  因为译者的水平缓技能才能有限,译稿虽经屡次校正,不免有遗漏之处,敬请咱们批判和纠正。若有发现不当之处,请发送邮件至 FrankieGao123@gmail.com,我会在 blog 中进行订正,谢谢!

    火龙果磕头!

2008 年 2 月 27 日



   *  这是由《程序员》杂志社评出的,刊登在《程序员》2007 年 3 月刊上。这七种根本技能是:数组,字符串与哈希表、正则表达式、调试、两门言语、一个开发环境、SQL 言语和编写软件的思维。

目录
     本文介绍怎么运用 java.util.regex API 作为正则表达式办法匹配。尽管说这个包中可被承受的语法参数与 Perl 是相似的,但咱们并不需求把握 Perl 的语法常识。本教程将从根底开端,逐层深化到更多的高档技巧。下面是各章节的首要内容:
188bet www.188bet.com bwin 平博 unibet 明升 188bet uk Ladbrokes 德赢vwin 188bet m88.com w88 平博88 uedbet体育 188bet 188bet 威廉希尔 明升体育app 平博88 M88 Games vwin德赢 uedbet官网 bodog fun88 188bet
0 导言
  粗略地看一下正则表达式,一同也介绍组成 API 的中心类。
1 测验用具
  编写了一个简略的运用程序,用于测验正则表达式的办法匹配。
2 字符串
  介绍根本的办法匹配、元字符和引证。
3 字符类
  描绘简略字符类、否定、规模、并集、交集和差集。
4 预界说字符类
  描绘空白字符、字母和数字字符等根本的预界说字符。
5 量词
  运用贪婪(greedy)、牵强(reluctant)和侵吞(possessive)量词,来匹配指定表达式 X 的次数。
6 捕获组
  解说怎么把多个字符作为一个独自的单元进行处理。
7 鸿沟匹配器
  描绘行、单词和输入的鸿沟。
8 Pattern 类的办法
  测验了 Pattern 中一些有用的办法,以及探求一些高档的特性,比方:带符号的编译和运用内嵌符号表达式。
9 Matcher 类的办法
  描绘了 Matcher 类中一般运用的办法。
10 PatternSyntaxException 类的办法
  描绘了怎么查看一个 PatternSyntaxException 反常。
11 更多的资源
  要了解更多正则表达式,能够参阅这一节。
12 问题和操练
  稳固一下本教程所介绍的正则表达式的根本常识,并附有答案。

  为了区别文档中的正则表达式和一般字符串,均以\d[abc]{2}的办法表明正则表达式的办法。
0 导言回来目录
0.1 什么是正则表达式?回来目录
  正则表达式(regular expressions)是一种描绘字符串集的办法,它是以字符串会集各字符串的共有特征为依据的。正则表达式能够用于查找、批改或许是操作文本和数据。它超出了 Java 程序设计言语的规范语法,因而有必要去学习特定的语法来构建正则表达式。正则表达式的改变是杂乱的,一旦你了解了它们是怎么被结构的话,你就能解析或许构建恣意的正则表达式了。
  本教程教学 java.util.regex API 所支撑的正则表达式语法,以及介绍几个可运转的比方来阐明不同的方针间是怎么交互的。在正则表达式的国际中,有不同风格的挑选,比方:grep[2]、Perl、Tcl、Python、PHP 和 awk。java.util.regex API 中的正则表达式语法与 Perl 中的最为相似。
0.2 java.util.regex 包是怎么描绘正则表达式的?回来目录
  java.util.regex 包首要由三个类所组成:Pattern、Matcher 和 PatternSyntaxException。
  • Pattern 方针表明一个已编译的正则表达式。Pattern 类没有供给公共的结构办法。要构建一个办法,首要有必要调用公共的静态 compile 办法,它将回来一个 Pattern 方针。这个办法承受正则表达式作为榜首个参数。本教程的开端部分将教你必需的语法。
  • Matcher 是一个靠着输入的字符串来解析这个办法和完结匹配操作的方针。与 Pattern 相似,Matcher 也没有界说公共的结构办法,需求经过调用 Pattern 方针的 matcher 办法来取得一个 Matcher 方针。
  • PatternSyntaxException 方针是一个未查看反常,指示了正则表达式中的一个语法过错。
  本教程的终究几节课程会详细地阐明各个类。首战之地的问题是:有必要了解正则表达式是怎么被构建的,因而下一节引入了一个简略的测验用具,重复地用于探求它们的语法。
1 测验用具回来目录
  这节给出了一个可重用的测验用具 RegexTestHarness.java,用于探求构建 API 所支撑的正则表达式。运用
java RegexTestHarness
这个指令来运转,没有被承受的指令行参数。这个运用会不停地循环履行下去[3],提示用户输入正则表达式和字符串。尽管说运用这个测验用具是可选的,但你会发现它用于探求下文所评论的测验用例将更为便利。
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");
            }
        }
    }
}
  在持续下一节之前,承认开发环境支撑必需的包,并保存和编译这段。
【译者注】
  因为当时版别的 Java Tutorial 是依据 JDK 6.0 编写的,上述的测验用具因为运用到 JDK 6.0 中新增的类库(java.io.Console),所以该用具只能在 JDK 6.0 的环境中编译运转,因为 Console 拜访操作系统平台上的操控台,因而这个测验用具只能在操作系统的字符操控台中运转,不能运转在 IDE 的操控台中。
  正则表达式是 JDK 1.4 所添加的类库,为了兼容 JDK 1.4 和 JDK 5.0 的版别,从头改写了这个测验用具,让其能适用于不同的版别。
  JDK 5.0 适用的测验用具(RegexTestHarnessV5.java,该用具能够在 IDE 中履行),主张 JDK 6.0 环境也选用该用具。
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexTestHarnessV5 {

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.printf("%nEnter your regex: ");
            Pattern pattern = Pattern.compile(scanner.nextLine());
            System.out.printf("Enter input string to search: ");
            Matcher matcher = pattern.matcher(scanner.nextLine());
            boolean found = false;
            while (matcher.find()) {
                System.out.printf(
                        "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) {
                System.out.printf("No match found.%n");
            }
        }
    }
}
  JDK 1.4 适用的测验用具(RegexTestHarnessV4.java):
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexTestHarnessV4 {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(
                new InputStreamReader(new BufferedInputStream(System.in))
            );
        while (true) {
            System.out.print("\nEnter your regex: ");
            Pattern pattern = Pattern.compile(br.readLine());
            System.out.print("Enter input string to search: ");
            Matcher matcher = pattern.matcher(br.readLine());
            boolean found = false;
            while (matcher.find()) {
                System.out.println("I found the text \"" + matcher.group() +
                        "\" starting at index " + matcher.start() +
                        " and ending at index " + matcher.end() +
                        ".");
                found = true;
            }
            if (!found) {
                System.out.println("No match found.");
            }
        }
    }
}
2 字符串回来目录
  在大多数的状况下,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。这个是约定俗成的,规模包含开端的索引,不包含完毕的索引,如下图所示:

图 1 字符串“foo”的单元格编号和索引值[4]
  字符串中的每一个字符坐落其自身的单元格(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.
2.1 元字符回来目录
  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.
  尽管在输入的字符串中没有点(.),但这个匹配仍然是成功的。这是因为点(.)是一个元字符(metacharacters)(被这个匹配翻译成了具有特别含义的字符了)。这个比方为什么能匹配成功的原因在于,元字符.指的是“恣意字符”。
  API 所支撑的元字符有:([{\^-$|}])?*+.

留意:在学习过更多的怎么构建正则表达式后,你会碰到这些状况:上面的这些特别字符不该该被处理为元字符。可是也能够运用这个清单来查看一个特别的字符是否会被认为是元字符。例如,字符 !、@ 和 # 决不会有特别的含义。

  有两种办法能够强制将元字符处理成为一般字符:
  1. 在元字符前加上反斜线(\);
  2. 把它放在\Q(引证开端)和\E(引证完毕)之间[5]。在运用这种技能时,\Q\E能被放于表达式中的任何方位(假定先呈现\Q[6]
3 字符类回来目录
  假如你曾看过 Pattern 类的阐明,会看到一些构建正则表达式的概述。在这一节中你会发现下面的一些表达式:
字符类
[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]] 除 b 和 c 之外的 a 到 z 字符:[ad-z](差集)
[a-z&&[^m-p]] a 到 z,而且不包含 m 到 p:[a-lq-z](差集)
  左面列指定正则表达式结构,右边列描绘每个结构的匹配的条件。

留意:“字符类(character class)”这个词中的“类(class)”指的并不是一个 .class 文件。在正则表达式的语义中,字符类是放在方括号里的字符集,指定了一些字符中的一个能被给定的字符串所匹配。

3.1 简略类(Simple Classes)回来目录
  字符类最根本的格局是把一些字符放在一对方括号内。例如:正则表达式[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.
  在上面的比方中,在榜首个字符匹配字符类中所界说字符中的一个时,整个匹配便是成功的。
3.1.1 否定回来目录
  要匹配除那些列表之外一切的字符时,能够在字符类的开端处加上^元字符,这种就被称为否定(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.
  在输入的字符串中的榜首个字符不包含在字符类中所界说字符中的一个时,匹配是成功的。
3.1.2 规模回来目录
  有时会想要界说一个包含值规模的字符类,比方,“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.
3.1.3 并集回来目录
  能够运用并集(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.
3.1.4 交集回来目录
  建一个只是匹配自身嵌套类中公共部分字符的字符类时,能够像[0-9&&[345]]中那样运用&&。这种办法构建出来的交集(intersection)简略字符类,只是以匹配两个字符类中的 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.
3.1.5 差集回来目录
  终究,能够运用差集(subtraction)来否定一个或多个嵌套的字符类,比方:[0-9&&[^345]],这个是构建一个匹配除 3,4,5 之外一切 0 到 9 间数字的简略字符类。
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.
  到此停止,现已包含了怎么树立字符类的部分。在持续下一节之前,能够试着回想一下那张字符类表
4 预界说字符类回来目录
  Pattern 的 API 包有许多有用的预界说字符类(predefined character classes),供给了常用正则表达式的简写办法。
预界说字符类
. 任何字符(匹配或许不匹配行完毕符)
\d 数字字符:[0-9]
\D 非数字字符:[^0-9]
\s 空白字符:[\t\n\x0B\f\r]
\S 非空白字符:[^\s]
\w 单词字符:[a-zA-Z_0-9]
\W 非单词字符:[^\w]
  上表中,左列是结构右列字符类的简写办法。例如:\d指的是数字规模(0~9),\w指的是单词字符(任何大小写字母、下划线或许是数字)。不论何时都有或许运用预界说字符类,它能够使更易阅览,更易从丑陋的字符类中排除过错。
  以反斜线(\)开端的结构称为转义结构(escaped constructs)。回忆一下在 字符串 一节中的转义结构,在那里咱们提及了运用反斜线,以及用于引证的\Q\E。在字符串中运用转义结构,有必要在一个反斜线前再添加一个反斜用于字符串的编译,例如:
private final String REGEX = "\\d";        // 单个数字
  这个比方中\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.
  在开端的三个比方中,正则表达式是简略的,.(“点”元字符)表明“恣意字符”,因而,在一切的三个比方(随意地选取了“@”字符,数字和字母)中都是匹配成功的。在接下来的比方中,都运用了预界说字符类表格中的单个正则表达式结构。你应该能够依据这张表指出前面每个匹配的逻辑:
  \d 匹配数字字符
  \s 匹配空白字符
  \w 匹配单词字符
  也能够运用意思正好相反的大写字母:
  \D 匹配非数字字符
  \S 匹配非空白字符
  \W 匹配非单词字符
5 量词回来目录
  这一节咱们来看一下贪婪(greedy)、牵强(reluctant)和侵吞(possessive)量词,来匹配指定表达式X的次数。
  量词(quantifiers)答应指定匹配呈现的次数,便利起见,当时 Pattern API 规范下,描绘了贪婪、牵强和侵吞三种量词。首要粗略地看一下,量词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后边跟着?*+。接下来看一下,用这些表达式来测验输入的字符串是空字符串时会发作些什么:
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.
5.1 零长度匹配回来目录
  在上面的比方中,开端的两个匹配是成功的,这是因为表达式a?a*都答应字符呈现零次。就现在而言,这个比方不像其他的,或许你留意到了开端和完毕的索引都是 0。输入的空字符串没有长度,因而该测验简略地在索引 0 上匹配什么都没有,比方此类的匹配称之为零长度匹配(zero-length matches)。零长度匹配会呈现在以下几种状况:输入空的字符串、在输入字符串的开端处、在输入字符串终究字符的后边,或许是输入字符串中恣意两个字符之间。因为它们开端和完毕的方位有着相同的索引,因而零长度匹配是简略被发现的。
  咱们来看一下关于零长度匹配更多的比方。把输入的字符串改为单个字符“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 处找到了零长度匹配,也便是说,在输入字符串终究一个字符的后边。回想一下,匹配把字符“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”的字母时会有什么成果。例如,在“ababaaaab”中遇到了字母“b”会发作什么呢?
  下面咱们来看一下:
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”。榜初次测验失利的原由在于,输入的字符串没有满意的 a 用来匹配;第2次测验输出的字符串正好包含了三个“a”,触发了一次匹配;第三次测验也触发了一次匹配,这是因为在输出的字符串的开端部分正好有三个“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.
  输入相同的字符串,这次测验只是找到了一个匹配,这是因为一个中有九个“a”满意了“至少”三个“a”的要求。
  终究,关于指定呈现次数的上限,能够在花括号添加第二个数字。
Enter your regex: a{3,6} // 寻觅一行中至少接连呈现 3 个(但不多于 6 个)“a”
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。
5.2 捕获组和字符类中的量词回来目录
  到现在停止,只是测验了输入的字符串包含一个字符的量词。实际上,量词只是或许附在一个字符后边一次,因而正则表达式abc+的意思便是“a 后边接着 b,再接着一次或许屡次的 c”,它的意思并不是指abc一次或许屡次。可是,量词也或许附在字符类和捕获组的后边,比方,[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”上。
5.3 贪婪、牵强和侵吞量词间的不同回来目录
  在贪婪、牵强和侵吞三个量词间有着纤细的不同。
  贪婪量词之所以称之为“贪婪的”,这是因为它们逼迫匹配器读入(或许称之为吃掉)整个输入的字符串,来优先测验榜初次匹配,假如榜初次测验匹配(关于整个输入的字符串)失利,匹配器会经过回退整个字符串的一个字符再一次进行测验,不断地进行处理直到找到一个匹配,或许左面没有更多的字符来用于回退了。赖于在表达式中运用的量词,终究它将测验地靠着 1 或 0 个字符的匹配。
  可是,牵强量词选用相反的途径:从输入字符串的开端处开端,因而每次牵强地吞噬一个字符来寻觅匹配,终究它们会测验整个输入的字符串。
  终究,侵吞量词始终是吞掉整个输入的字符串,测验着一次(仅有一次)匹配。不像贪婪量词那样,侵吞量词绝不会回退,即便这样做是答应悉数的匹配成功。
  为了阐明一下,看看输入的字符串是 xfooxxxxxxfoo 时。
Enter your regex: .*foo  // 贪婪量词
Enter input string to search: xfooxxxxxxfoo
I found the text "xfooxxxxxxfoo" starting at index 0 and ending at index 13.

Enter your regex: .*?foo  // 牵强量词
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 // 侵吞量词
Enter input string to search: xfooxxxxxxfoo
No match found.
  榜首个比方运用贪婪量词.*,寻觅紧跟着字母“f”“o”“o”的“任何东西”零次或许屡次。因为量词是贪婪的,表达式的.*部分榜初次“吃掉”整个输入的字符串。在这一点,悉数表达式不能成功地进行匹配,这是因为终究三个字母(“f”“o”“o”)现已被耗费掉了。那么匹配器会慢慢地每次回退一个字母,直到返还的“foo”在最右边呈现,这时匹配成功而且查找停止。
  可是,第二个比方选用牵强量词,因而经过初次耗费“什么也没有”作为开端。因为“foo”并没有呈现在字符串的开端,它被逼迫吞掉榜首个字母(“x”),在 0 和 4 处触发了榜首个匹配。测验用具会持续处理,直到输入的字符串耗尽停止。在 4 和 13 找到了别的一个匹配。
  第三个比方的量词是侵吞,所以在寻觅匹配时失利了。在这种状况下,整个输入的字符串被.*+耗费了,什么都没有剩下来满意表达式完毕的“foo”。
  你能够在想抓取一切的东西,且决不回退的状况下运用侵吞量词,在这种匹配不是当即被发现的状况下,它将会优于等价的贪婪量词。
6 捕获组回来目录
  在上一节中,学习了每次怎么把量词放在一个字符、字符类或许捕获组中。到现在停止,还没有详细地评论过捕获组的概念。
  捕获组(capturing group)是将多个字符作为独自的单元来对待的一种办法。构建它们能够经过把字符放在一对圆括号中而成为一组。例如,正则表达式(dog)建了单个的组,包含字符“d”“o”和“g”。匹配捕获组输入的字符串部分将会存放于内存中,稍后经过反向引证再次调用。(在 6.2 节 中将会评论反向引证)
6.1 编号办法回来目录
  在 Pattern 的 API 描绘中,捕获组经过从左至右核算开端的圆括号进行编号。例如,在表达式((A)(B(C)))中,有下面的四组:
  1. ((A)(B(C)))
  2. (A)
  3. (B(C))
  4. (C)
  要找出当时的表达式中有多少组,经过调用 Matcher 方针的 groupCount 办法。groupCount 办法回来 int 类型值,表明当时 Matcher 办法中捕获组的数量。例如,groupCount 回来 4 时,表明办法中包含有 4 个捕获组。
  有一个特别的组——组 0,它表明整个表达式。这个组不包含在 groupCount 的陈述规模内。以(?开端的组是朴实的非捕获组(non-capturing group),它不捕获文本,也不作为组总数而计数。(能够看 8 Pattern 类的办法 一节中非捕获组的比方。)
  Matcher 中的一些办法,能够指定 int 类型的特定组号作为参数,因而了解组是怎么编号的是尤为重要的。
  :回来之前的匹配操作期间,给定组所捕获的子序列的初始索引。
  :回来之前的匹配操作期间,给定组所捕获子序列的终究字符索引加 1。
  :回来之前的匹配操作期间,经过给定组而捕获的输入子序列。
6.2 反向引证回来目录
  匹配输入字符串的捕获组部分会存放在内存中,经过反向引证(backreferences)稍后再调用。在正则表达式中,反向引证运用反斜线(\)后跟一个表明需求再调用组号的数字来表明。例如,表达式(\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.
  关于嵌套的捕获组而言,反向引证选用彻底相同的办法进行作业,即指定一个反斜线加上需求被再次调用的组号。
7 鸿沟匹配器回来目录
  就现在而言,咱们的爱好在于指定输入字符串中某些方位是否有匹配,还没有考虑到字符串的匹配发生在什么地方。
  经过指定一些鸿沟匹配器(boundary matchers)的信息,能够使办法匹配更为准确。比方说你对某个特定的单词感爱好,而且它只呈现内行首或许是行尾时。又或许你想知道匹配发作在单词鸿沟(word boundary),或许是上一个匹配的尾部。
  下表中列出了一切的鸿沟匹配器及其阐明。
鸿沟匹配器
^ 行首
$ 行尾
\b 单词鸿沟
\B 非单词鸿沟
\A 输入的开端
\G 上一个匹配的完毕
\Z 输入的完毕,仅用于终究的完毕符(假如有的话)
\z 输入的完毕
  接下来的比方中,阐明晰^$鸿沟匹配器的用法。留意上表中,^匹配行首,$匹配行尾。
Enter your regex: ^dog$
Enter input string to search: dog
I found the text "dog" starting at index 0 and ending at index 3.

Enter your regex: ^dog$
Enter input string to search:       dog
No match found.

Enter your regex: \s*dog$
Enter input string to search:             dog
I found the text "            dog" starting at index 0 and ending at index 15.

Enter your regex: ^dog\w*
Enter input string to search: dogblahblah
I found the text "dogblahblah" starting at index 0 and ending at index 11.
  榜首个比方的匹配是成功的,这是因为办法占有了整个输入的字符串。第二个比方失利了,是因为输入的字符串在开端部分包含了额定的空格。第三个比方指定的表达式是不限的空格,后跟着内行尾的 dog。第四个比方,需求 dog 放内行首,后边跟的是不限数量的单词字符。
  关于查看一个单词开端和完毕的鸿沟办法(用于长字符串里子字符串),这时能够在两头运用\b,例如\bdog\b
Enter your regex: \bdog\b
Enter input string to search: The dog plays in the yard.
I found the text "dog" starting at index 4 and ending at index 7.

Enter your regex: \bdog\b
Enter input string to search: The doggie plays in the yard.
No match found.
  关于匹配非单词鸿沟的表达式,能够运用\B来替代:
Enter your regex: \bdog\B
Enter input string to search: The dog plays in the yard.
No match found.

Enter your regex: \bdog\B
Enter input string to search: The doggie plays in the yard.
I found the text "dog" starting at index 4 and ending at index 7.
  关于需求匹配仅呈现在前一个匹配的完毕,能够运用\G
Enter your regex: dog
Enter input string to search: dog dog
I found the text "dog" starting at index 0 and ending at index 3.
I found the text "dog" starting at index 4 and ending at index 7.

Enter your regex: \Gdog
Enter input string to search: dog dog
I found the text "dog" starting at index 0 and ending at index 3.
  这儿的第二个比方仅找到了一个匹配,这是因为第2次呈现的“dog”不是在前一个匹配完毕的开端。[7]
8 Pattern 类的办法回来目录
  到现在停止,仅运用测验用具来树立最根本的 Pattern 方针。在这一节中,咱们将讨论一些比方运用标志构建办法、运用内嵌标志表达式等高档的技能。一同也讨论了一些现在还没有评论过的其他有用的办法。
8.1 运用标志构建办法回来目录
  Pattern 类界说了备用的 compile 办法,用于承受影响办法匹配办法的标志集。标志参数是一个位掩码,能够是下面公共静态字段中的恣意一个:
Pattern.CANON_EQ
  启用规范等价。在指定此标志后,当且仅当在其完好的规范分化匹配时,两个字符被视为匹配。例如,表达式a\u030A[8]在指定此标志后,将匹配字符串“\u00E5”(即字符 å)。默许状况下,匹配不会选用规范等价。指定此标志或许会对功用会有必定的影响。
Pattern.CASE_INSENSITIVE
  启用不区别大小写匹配。默许状况下,仅匹配 US-ASCII 字符会集的字符。Unicode 感知(Unicode-aware)的不区别大小写匹配,能够经过指定 UNICODE_CASE 标志连同此标志来启用。不区别大小写匹配也能经过内嵌标志表达式(?i)来启用。指定此标志或许会对功用会有必定的影响。
Pattern.COMMENTS
  办法中答应存在空白和注释。在这种办法下,空白和以#开端的直到行尾的内嵌注释会被疏忽。注释办法也能经过内嵌标志表达式(?x)来启用。
Pattern.DOTALL
  启用 dotall 办法。在 dotall 办法下,表达式.匹配包含行完毕符在内的恣意字符。默许状况下,表达式不会匹配行完毕符。dotall 办法也经过内嵌标志表达式(?x)来启用。[s 是“单行(single-line)”办法的助记符,与 Perl 中的相同。]
Pattern.LITERAL
  启用办法的字面剖析。指定该标志后,指定办法的输入字符串作为字面上的字符序列来对待。输入序列中的元字符和转义字符不具有特别的含义了。CASE_INSENSITIVE 和 UNICODE_CASE 与此标志一同运用时,会对匹配发生必定的影响。其他的标志就变得剩余了。启用字面剖析没有内嵌标志表达式。
Pattern.MULTILINE
  启用多行(multiline)办法。在多行办法下,表达式^$别离匹配输入序列行完毕符前面和行完毕符的前面。默许状况下,表达式仅匹配整个输入序列的开端和完毕。多行办法也能经过内嵌标志表达式(?m)来启用。
Pattern.UNICODE_CASE
  启用可折叠感知 Unicode(Unicode-aware case folding)大小写。在指定此标志后,需求经过 CASE_INSENSITIVE 标志来启用,不区别大小写区配将在 Unicode 规范的含义上来完结。默许状况下,不区别大小写匹配仅匹配 US-ASCII 字符会集的字符。可折叠感知 Unicode 大小写也能经过内嵌标志表达式(?u)来启用。指定此标志或许会对功用会有必定的影响。
Pattern.UNIX_LINES
  启用 Unix 行办法。在这种办法下,.^$的行为仅辨认“\n”的行完毕符。Unix 行办法能够经过内嵌标志表达式(?d)来启用。
  接下来,将批改测验用具 RegexTestHarness.java,用于构建不区别大小写匹配的办法。
  首要,批改去调用 complie 的别的一个备用的办法:
Pattern pattern = Pattern.compile(
        console.readLine("%nEnter your regex: "),
        Pttern.CASE_INSENSITIVE
    );
  编译并运转这个测验用具,会得出下面的成果:
Enter your regex: dog
Enter input string to search: DoGDOg
I found the text "DoG" starting at index 0 and ending at index 3.
I found the text "DOg" starting at index 3 and ending at index 6.
  正如你所看到的,不论是否大小写,字符串字面上是“dog”的都发生了匹配。运用多个标志来编译一个办法,运用按位或操作符“|”分隔各个标志。为了更明晰地阐明,下面的示例运用硬编码(hardcode)的办法,来替代操控台中的读取:
pattern = Pattern.compile("[az]$", Pattern.MULTILINE | Pattern.UNIX_LINES);
  也能够运用一个 int 类型的变量来替代:
final int flags = Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE;
Pattern pattern = Pattern.compile("aa", flags);
8.2 内嵌标志表达式回来目录
  运用内嵌标志表达式(embedded flag expressions)也能够启用不同的标志。关于两个参数的 compile 办法,内嵌标志表达式是可选的,因为它在自身的正则表达式中被指定了。下面的比方运用开端的测验用具(RegexTestHarness.java),运用内嵌标志表达式(?i)来启用不区别大小写的匹配。
Enter your regex: (?i)foo
Enter input string to search: FOOfooFoOfoO
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.
I found the text "foO" starting at index 9 and ending at index 12.
  一切匹配无关大小写都一次次地成功了。
  内嵌标志表达式所对应 Pattern 的共用的拜访字段表明如下表:
常  量 等价的内嵌标志表达式
Pattern.CANON_EQ 没有
Pattern.CASE_INSENSITIVE (?i)
Pattern.COMMENTS (?x)
Pattern.MULTILINE (?m)
Pattern.DOTALL (?s)
Pattern.LITERAL 没有
Pattern.UNICODE_CASE (?u)
Pattern.UNIX_LINES (?d)
8.3 运用 matches(String, CharSequence) 办法回来目录
  Pattern 类界说了一个便利的 matches 办法,用于快速地查看办法是否表明给定的输入字符串。与运用一切的公共静态办法相同,应该经过它的类名来调用 matches 办法,比方 Pattern.matches("\\d","1");。这个比方中,办法回来 true,这是因为数字“1”匹配了正则表达式\d
8.4 运用 split(String) 办法回来目录
  split 办法是一个重要的东西,用于搜集依赖于被匹配的办法任一边的文本。如下面的 SplitDemo.java 所示,split 办法能从“one:two:three:four:five”字符串中解分出“one two three four five”单词:
import java.util.regex.Pattern;

public class SplitDemo {

    private static final String REGEX = ":";
    private static final String INPUT = "one:two:three:four:five";
    
    public static void main(String[] args) {
        Pattern p = Pattern.compile(REGEX);
        String[] items = p.split(INPUT);
        for(String s : items) {
            System.out.println(s);
        }
    }
}
  输出:
one
two
three
four
five
  简而言之,现已运用冒号(:)替代了杂乱的正则表达式匹配字符串文字。往后仍会运用 Pattern 和 Matcher 方针,也能运用 split 得到坐落恣意正则表达式各边的文本。下面的 SplitDemo2.java 是个相同的比方,运用数字作为 split 的参数:
import java.util.regex.Pattern;

public class SplitDemo2 {

    private static final String REGEX = "\\d";
    private static final String INPUT = "one9two4three7four1five";

    public static void main(String[] args) {
        Pattern p = Pattern.compile(REGEX);
        String[] items = p.split(INPUT);
        for(String s : items) {
            System.out.println(s);
        }
    }
}
  输出:
one
two
three
four
five
8.5 其他有用的办法回来目录
  你能够从下面的办法中找到比较好用的办法:
  [9]:回来指定字符串字面办法的字符串。此办法会发生一个字符串,能被用于构建一个与字符串 s 匹配的 Pattern,如同它是一个字面上的办法。输入序列中的元字符和转义序列将没有特别的含义了。
  :回来这个办法的字符串体现办法。这是一个编译过的办法中的正则表达式。
8.6 在 java.lang.String 中等价的 Pattern 办法回来目录
  java.lang.String 经过仿照 java.util.regex.Pattern 行为的几个办法,也能够支撑正则表达式。便利起见,下面首要摘录了呈现在 API 要害的办法。
  :奉告字符串是否匹配给定的正则表达式。调用 str.matches(regex)办法所发生的成果与作为表达式的 Pattern.matches(regex, str)的成果是彻底一致。
  :依照匹配给定的正则表达式来拆分字符串。调用 str.split(regex, n)办法所发生的成果与作为表达式的 Pattern.compile(regex).split(str, n) 的成果彻底一致。
  :依照匹配给定的正则表达式来拆分字符串。这个办法与调用两个参数的 split 办法是相同的,榜首个参数运用给定的表达式,第二个参数限制为 0。在成果数组中不包含尾部的空字符串。
  还有一个替换办法,把一个 CharSequence 替换成别的一个:
  :将字符串中每一个匹配替换匹配字面方针序列的子字符串,替换成指定的字面替换序列。这个替换从字符串的开端处理直至完毕,例如,把字符串“aaa”中的“aa”替换成“b”,成果是“ba”,而不是“ab”。
9 Matcher 类的办法回来目录
  在这一节中来看看 Matcher 类中其他一些有用的办法。便利起见,下面列出的办法是依照功用来分组的。
索引办法
  索引办法(index methods)供给了一些正好在输入字符串中发现匹配的索引值:
  :回来之前匹配的开端索引。
  :回来之前匹配操作中经过给定组所捕获序列的开端索引。
  : 回来终究匹配字符后的偏移量。
  : 回来之前匹配操作中经过给定组所捕获序列的终究字符之后的偏移量。
研讨办法
  研讨办法(study methods)回忆输入的字符串,而且回来一个用于指示是否找到办法的布尔值。
  : 测验从区域开端处开端,输入序列与该办法匹配。
  : 测验地寻觅输入序列中,匹配办法的下一个子序列。
  : 重置匹配器,然后从指定的索引处开端,测验地寻觅输入序列中,匹配办法的下一个子序列。
  : 测验将整个区域与办法进行匹配
替换办法
  替换办法(replacement methods)用于在输入的字符串中替换文本有用途的办法。
  :完结非完毕处的添加和替换操作。
  :完结完毕处的添加和替换操作。
  :运用给定的替换字符串来替换输入序列中匹配办法的每一个子序列。
  :运用给定的替换字符串来替换输入序列中匹配办法的榜首个子序列。
  :回来指定字符串的字面值来替换字符串。这个办法会生成一个字符串,用作 Matcher 的 appendReplacement 办法中的字面值替换 s。所发生的字符串将与作为字面值序列的 s 中的字符序列匹配。斜线(\)和美元符号($)将不再有特别含义了。
9.1 运用 start 和 end 办法回来目录
  示例程序 MatcherDemo.java 用于核算输入序列中单词“dog”的呈现次数。
import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class MatcherDemo {

    private static final String REGEX = "\\bdog\\b";
    private static final String INPUT = "dog dog dog doggie dogg";

    public static void main(String[] args) {
       Pattern p = Pattern.compile(REGEX);
       Matcher m = p.matcher(INPUT);        // 取得匹配器方针
       int count = 0;
       while (m.find()) {
           count++;
           System.out.println("Match number " + count);
           System.out.println("start(): " + m.start());
           System.out.println("end(): " + m.end());
       }
    }
}
  输出:
Match number 1
start(): 0
end(): 3
Match number 2
start(): 4
end(): 7
Match number 3
start(): 8
end(): 11
  能够看出,这个比方运用了单词鸿沟,用于保证更长单词中的字母“d”“o”“g”就不是子串了。它也输出了一些有用的信息,在输入的字符串中什么地方有匹配。start 办法回来在曾经的匹配操作期间,由给定组所捕获子序列的开端处索引,end 办法回来匹配到终究一个字符索引加 1。
9.2 运用 matches 和 lookingAt 办法回来目录
  matches 和 lookingAt 办法都是测验该办法匹配输入序列。可是不同的是,matches 要求匹配整个输入字符串,而 lookingAt 不是这样。这两个办法都是从输入字符串的开端开端的。下面是 MatchesLooking.java 完好的:
import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class MatchesLooking {

    private static final String REGEX = "foo";
    private static final String INPUT = "fooooooooooooooooo";
    private static Pattern pattern;
    private static Matcher matcher;

    public static void main(String[] args) {
   
        // 初始化
        pattern = Pattern.compile(REGEX);
        matcher = pattern.matcher(INPUT);

        System.out.println("Current REGEX is: " + REGEX);
        System.out.println("Current INPUT is: " + INPUT);

        System.out.println("lookingAt(): " + matcher.lookingAt());
        System.out.println("matches(): " + matcher.matches());
    }
}
  输出:
Current REGEX is: foo
Current INPUT is: fooooooooooooooooo
lookingAt(): true
matches(): false
9.3 运用 replaceFirst(String) 和 replaceAll(String) 办法回来目录
  replaceFirst 和 replaceAll 办法替换匹配给定正则表达式的文本。从它们的姓名能够看出,replaceFirst 替换榜首个匹配到的,而 replaceAll 替换一切匹配的。下面是 ReplaceDemo.java 的:
import java.util.regex.Pattern; 
import java.util.regex.Matcher;

public class ReplaceDemo {
 
    private static String REGEX = "dog";
    private static String INPUT = "The dog says meow. All dogs say meow.";
    private static String REPLACE = "cat";
 
    public static void main(String[] args) {
        Pattern p = Pattern.compile(REGEX);
        Matcher m = p.matcher(INPUT);       // 取得匹配器方针
        INPUT = m.replaceAll(REPLACE);
        System.out.println(INPUT);
    }
}
  输出:
The cat says meow. All cats say meow.
  在上面的比方中,一切的 dog 都被替换成了 cat。可是为什么在这儿停下来了呢?你能够替换匹配任何正则表达式的文本,这样优于替换一个简略的像 dog 相同的文字。这个办法的 API 描绘了“给定正则表达式a*b,在输入‘aabfooaabfooabfoob’和替换的字符串是‘-’状况下,表达式的匹配器调用办法后,会发生成字符串‘-foo-foo-foo-’。”
  下面是 ReplaceDemo2.java 的:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
 
public class ReplaceDemo2 {
 
    private static String REGEX = "a*b";
    private static String INPUT = "aabfooaabfooabfoob";
    private static String REPLACE = "-";
 
    public static void main(String[] args) {
        Pattern p = Pattern.compile(REGEX);
        Matcher m = p.matcher(INPUT);       // 取得匹配器方针
        INPUT = m.replaceAll(REPLACE);
        System.out.println(INPUT);
    }
}
  输出:
-foo-foo-foo-
  仅要替换办法一次时,能够简略地调用 replaceFirst 用于替代 replaceAll,它承受相同的参数。
9.4 运用 appendReplacement(StringBuffer, String) 和
      appendTail(StringBuffer) 办法回来目录
  Matcher 类也供给了 appendReplacement 和 appendTail 两个办法用于文本替换。下面的这个比方(RegexDemo.java)运用了这两个办法完结与 replaceAll 相同的功用。
import java.util.regex.Pattern;
import java.util.regex.Matcher;

public class RegexDemo {
 
    private static String REGEX = "a*b";
    private static String INPUT = "aabfooaabfooabfoob";
    private static String REPLACE = "-";
 
    public static void main(String[] args) {
        Pattern p = Pattern.compile(REGEX);
        Matcher m = p.matcher(INPUT);       // 取得匹配器方针
        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            m.appendReplacement(sb, REPLACE);
        }
        m.appendTail(sb);
        System.out.println(sb.toString());
    }
}
  输出:
-foo-foo-foo-
9.5 在 java.lang.String 中等价的 Matcher 办法回来目录
  为了运用便利,String 类看上去还不错地仿照了 Matcher 的两个办法:
  :运用给定的替换字符串替换该字符串中匹配了给定正则表达式的榜首个子字符串。调用 str.replaceFirst(regex, repl)办法与运用 Pattern.compile(regex).matcher(str).replaceFirst(repl)发生的成果是彻底相同的。
  :运用给定的替换字符串替换该字符串中匹配了给定正则表达式的每一个子字符串。调用 str.replaceAll(regex, repl)办法与运用 Pattern.compile(regex).matcher(str).replaceAll(repl)发生的成果是彻底相同的。
10 PatternSyntaxException 类的办法回来目录
  PatternSyntaxException 是未查看反常,指示正则表达式办法中的语法过错。PatternSyntaxException 类供给了下面的一些办法,用于确定在什么地方发作了过错:
  :取得过错描绘。
  :取得过错索引。
  :取得字符串办法的过错正则表达式。
  :取得一个多行的字符串,包含语法过错和过错的索引、过错的正则表达式办法,以及办法内可视化的索引指示。
  下面的源(RegexTestHarness2.java[10])更新了测验用具,用于查看不正确的正则表达式:
import java.io.Console;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.regex.PatternSyntaxException;

public class RegexTestHarness2 {

    public static void main(String[] args){
        Pattern pattern = null;
        Matcher matcher = null;

        Console console = System.console();
        if (console == null) {
            System.err.println("No console.");
            System.exit(1);
        }
        while (true) {
            try {
                pattern = Pattern.compile(console.readLine("%nEnter your regex: "));
                matcher = pattern.matcher(console.readLine("Enter input string to search: "));
            } catch (PatternSyntaxException pse){
                console.format("There is a problem with the regular expression!%n");
                console.format("The pattern in question is: %s%n", pse.getPattern());
                console.format("The description is: %s%n", pse.getDescription());
                console.format("The message is: %s%n", pse.getMessage());
                console.format("The index is: %s%n", pse.getIndex());
                System.exit(0);
            }
            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");
            }
        }
    }
}
  运转该测验,输入?i)foo作为正则表达式。这是个臆想出来的过错,程序员在运用内嵌标志表达式(?i)时忘掉输入左括号了。这样做会发生下面的成果:
Enter your regex: ?i)
There is a problem with the regular expression!
The pattern in question is: ?i)
The description is: Dangling meta character '?'
The message is: Dangling meta character '?' near index 0
?i)
^
The index is: 0
  从这个输出中,能够看出在索引 0 处的元字符(?)邻近有语法过错。短少左括号是导致这个过错的最魁祸首。
11 更多的资源回来目录
  现在现已完毕了正则表达式的课程,你或许会发现,首要引证了 Pattern、Matcher 和 PatternSyntaxException 类的 API 文档。
  构建正则表达式更详细地描绘,引荐阅览 Jeffrey E.F.Friedl 的Mastering Regular Expressions[11]
12 问题和操练回来目录
〖问题〗
1. 在 java.util.regex 包中有哪三个公共的类?描绘一下它们的效果。
2. 考虑一下字符串“foo”,它的开端索引是多少?完毕索引是多少?解说一下这些编号的意思。
3. 一般字符和元字符有什么不同?各给出它们的一个比方。
4. 怎么把元字符体现成像一般字符那样?
5. 附有方括号的字符集称为什么?它有什么效果?
6. 这儿是三个预界说的字符类:\d\s\w。描绘一下它们各表明什么?并运用方括号的办法将它们重写。
7. 关于\d\s\w,写出两个简略的表达式,匹配它们相反的字符集。
8. 考虑正则表达式(dog){3},辨认一下其间的两个子表达式。这个表达式会匹配什么字符串?
〖操练〗
1. 运用反向引证写一个表达式,用于匹配一个人的姓名,假定这个人的 first 姓名与 last 姓名是相同的。
【问题答案】
1. 在 java.util.regex 包中有哪三个公共的类?描绘一下它们的效果。
  • 编译后的 Pattern 实例表明正则表达式。
  • Matcher 实例是解析办法和靠着输入的字符串完结匹配操作的引擎。
  • PatternSyntaxException 界说一个未查看反常,指示正则表达式中的语法过错。
2. 考虑一下字符串“foo”,它的开端索引是多少?完毕索引是多少?解说一下这些编号的意思。
字符串中的每一个字符坐落其自身的单元格中。索引方位在两个单元格之间。字符串“foo”开端于索引 0,完毕于索引 3,即便是这些字符仅占用了 0、1 和 2 号单元格。
3. 一般字符和元字符有什么不同?各给出它们的一个比方。
正则表达式中的一般字符匹配其自身。元字符是一个特别的字符,会影响被匹配办法的办法。字母A是一个一般字符。标点符号.是一个元字符,其匹配恣意的单字符。
4. 怎么把元字符体现成像一般字符那样?
有两种办法:
  • 在元字符前加上反斜线(\);
  • 把元字符置于\Q(开端)\E(完毕)的引证表达式中。
5. 附有方括号的字符集称为什么?它有什么效果?
是一个字符类。经过方括号间的表达式,匹配指定字符类中的恣意一个字符。
6. 这儿是三个预界说的字符类:\d\s\w。描绘一下它们各表明什么?并运用方括号的办法将它们重写。
\d 匹配恣意数字[0-9]
  \s 匹配恣意空白字符[ \t\n-x0B\f\r]
  \w 匹配恣意单词字符[a-zA-Z_0-9]
7. 关于\d\s\w,写出两个简略的表达式,匹配它们相反的字符集。
\d \D [^\d]
  \s \S [^\s]
  \w \W [^\w]
8. 考虑正则表达式(dog){3},辨认一下其间的两个子表达式。这个表达式会匹配什么字符串?
表达式由捕获组(dog)和接着的贪婪量词{3}所组成。它匹配字符串“dogdogdog”。
【操练答案】
1. 运用反向引证写一个表达式,用于匹配一个人的姓名,假定这个人的 first 姓名与 last 姓名是相同的。
([A-Z][a-zA-Z]*)\s\1

[1]  本文全文译自 Java TutorialRegular Expressions,标题是译者自拟的。——译者注

[2]  Unix 东西,用于文件中的字符串查找,它是最早的正则表达式东西之一。——译者注

[3]  若要退出能够运用 Ctrl + C 来中止。——译者注

[4]  图中的“索引 3”指示是译者所加,原文中并没有。——译者注

[5]  这种办法在 JDK 6.0 曾经版别运用需求留意,在字符类中运用这种结构是有 bug 的,不过在 JDK 6.0 中现已批改。——译者注

[6]  若\E前没有\Q时会发生 PatternSyntaxException 反常指示语法过错。——译者注

[7]  榜初次匹配时仅匹配字符串的开端部分,与\A相似。(引自 Jeffrey E.F.Friedl, Mastering Regular Expressions, 3rd ed., §3.5.3.3, O'Reilly, 2006.)——译者注

[8]  \u030A,即字符 å 上半部分的小圆圈( ̊ )(该字符在 IE 浏览器上无法正确显现,在 Firefox 浏览器上能够正常地显现)。——译者注

[9]  JDK 5.0 新增的办法,JDK 1.4 中不能运用。——译者注

[10]  JDK 1.4 和 JDK 5.0 适用的版别在所附的源中。适用于 JDK 1.4 的文件名为 RegexTestHarness2V4.java,JDK 1.5 的文件名为 RegexTestHarness2V5.java。——译者注

[11]  第三版是本书的最新版别。第三版的中译本《通晓正则表达式》已由电子工业出书社于 2007 年 7 月出书。——译者注

译跋文回来目录
  带着坐卧不安的心境完结了我的榜首篇译篇,希望这个教程能让咱们对 Java 中的正则表达式有更一步的知道。
  尽管这是一个关于 Java 正则表达式很好的一个入门教程,但这个教程也有其不足之处,其间只是触及了最为简略的正则表达式,对介绍到的有些问题并未彻底打开,比方:字符类中的转义、内嵌标志表达式详细的用法等。对有些常用的表达式,如|(挑选结构)也没有触及。关于非捕获组来说,只是说到了内嵌标志表达式,关于比方(?:X)(?=X)(?!X)(?<=X)(?<!X)(?>X)等等之类的非捕获组结构彻底没有触及。正如译者在序中说到的,这篇文章只为往后学习更高档的正则表达式技能奠定杰出的根底。

译者
2008 年 3 月 2 日