不灭的焱

革命尚未成功,同志仍须努力下载JDK17

作者:Albert.Wen  添加时间:2019-02-03 00:18:25  修改时间:2024-04-11 12:58:14  分类:Java基础  编辑

在java.util.regex包中,包括了两个类:Pattern(模式类)Matcher(匹配器类)。Pattern类是用来表达和陈述所要搜索模式的对象,Matcher类是真正影响搜索的对象。另加一个新的模式异常类:PatternSyntaxException,当遇到不合法的搜索模式时,会抛出异常。

即使对正则表达式很熟悉,你会发现,通过Java使用正则表达式也相当简单。要说明的一点是,对那些被Perl的单行匹配所宠坏的Perl狂热爱好者来说,在使用Java的regex包进行替换操作时,会比他们所以前常用的方法费事些。我下面就举一些例子来教读者如何使用正则表达式,以及如何更简单地去使用它。

设计一个简单的表达式来匹配任何电话号码数字可能是比较复杂的事情,原因在于电话号码格式有很多种情况。所有必须选择一个比较有效的模式。比如:(212) 555-1212, 212-555-1212和212 555 1212,某些人会认为它们都是等价的。

首先让我们构成一个正则表达式。为简单起见,先构成一个正则表达式来识别下面格式的电话号码数字:(nnn) nnn-nnnn

第一步,创建一个pattern对象来匹配上面的子字符串。一旦程序运行后,如果需要的话,可以让这个对象一般化。匹配上面格式的正则表达可以这样构成:(\d{3})\s\d{3}-\d{4},其中\d单字符类型用来匹配从0到9的任何数字,另外{3}重复符号,是个简便的记号,用来表示有3个连续的数字位,也等效于(\d\d\d)。\s也另外一个比较有用的单字符类型,用来匹配空格,比如Space键,tab键和换行符。

是不是很简单?但是,如果把这个正则表达式的模式用在Java程序中,还要做两件事。对Java的解释器来说,在反斜线字符(\)前的字符有特殊的含义。

在Java中,与regex有关的包,并不都能理解和识别反斜线字符(\),尽管可以试试看。但为避免这一点,即为了让反斜线字符(\)在模式对象中被完全地传递,应该用双反斜线字符(\\)。

此外圆括号在正则表达中两层含义,如果想让它解释为字面上意思(即圆括号),也需要在它前面用双反斜线字符(\\),也就是像下面的一样:

\\(\\d{3}\\)\\s\\d{3}-\\d{4}

现在介绍怎样在Java代码中实现刚才所讲的正则表达式。要记住的事,在用正则表达式的包时,在你所定义的类前需要包含该包,也就是这样的一行:

import java.util.regex.*;

下面的一段代码实现的功能是,从一个文本文件逐行读入,并逐行搜索电话号码数字,一旦找到所匹配的,然后输出在控制台。

BufferedReader in = new BufferedReader(new FileReader("phone"));
Pattern pattern = Pattern.compile("\\(\\d{3}\\)\\s\\d{3}-\\d{4}");

String s;
while ((s = in.readLine()) != null) {
    Matcher matcher = pattern.matcher(s);

    if (matcher.find()) {
        System.out.println(matcher.group());
    }
}

in.close();

在Python和Javascript这些语言中,或者其他的语言,这些正则表达式一旦明确地编译过后,你想用到哪里都可以。与Perl的单步匹配相比,看起来多做了些工作,但这并不很费事。

  • find()方法,就像你所想象的,用来搜索与正则表达式相匹配的任何目标字符串,
  • group()方法,用来返回包含了所匹配文本的字符串。

应注意的是,上面的代码,仅用在每行只能含有一个匹配的电话号码数字字符串时。可以肯定的说,Java的正则表达式包能用在一行含有多个匹配目标时的搜索。本文的原意在于举一些简单的例子来激起读者进一步去学习Java自带的正则表达式包,所以对此就没有进行深入的探讨。

这相当漂亮吧! 但是很遗憾的是,这仅是个电话号码匹配器。很明显,还有两点可以改进。如果在电话号码的开头,即区位号和本地号码之间可能会有空格。我们也可匹配这些情况,则通过在正则表达式中加入\s?来实现,其中?元字符表示在模式可能有0或1个空格符。

第二点是,在本地号码位的前三位和后四位数字间有可能是空格符,而不是连字号,更有胜者,或根本就没有分隔符,就是7位数字连在一起。对这几种情况,我们可以用(-|)?来解决。这个结构的正则表达式就是转换器,它能匹配上面所说的几种情况。在()能含有管道符|时,它能匹配是否含有空格符或连字符,而尾部的?元字符表示是否根本没有分隔符的情况。

最后,区位号也可能没有包含在圆括号内,对此可以简单地在圆括号后附上?元字符,但这不是一个很好的解决方法。因为它也包含了不配对的圆括号,比如"(555" 或 "555)"。相反,我们可以通过另一种转换器来强迫让电话号码是否带有有圆括号:(\(\d{3}\)|\d{3})。如果我们把上面代码中的正则表达式用这些改进后的来替换的话,上面的代码就成了一个非常有用的电话号码数字匹配器:

Pattern pattern = Pattern.compile("(\\(\\d{3}\\)|\\d{3})\\s?\\d{3}(-|)?\\d{4}");

可以确定的是,你可以自己试着进一步改进上面的代码。

现在看看第二个例子,它是从Friedl的中改编过来的。其功能是用来检查文本文件中是否有重复的单词,这在印刷排版中会经常遇到,同样也是个语法检查器的问题。

匹配单词,像其他的一样,也可以通过好几种的正则表达式来完成。可能最直接的是\b\w+\b,其优点在于只需用少量的regex元字符。其中

  • \w元字符:用来匹配从字母a到u的任何字符;
  • +元字符:表示匹配匹配一次或多次字符;
  • \b元字符:是用来说明匹配单词的边界,它可以是空格或任何一种不同的标点符号(包括逗号,句号等)。

现在,我们怎样来检查一个给定的单词是否被重复了三次?为完成这个任务,需充分利用正则表达式中的所熟知的向后扫描。如前面提到的,圆括号在正则表达式中有几种不同的用法,一个就是能提供组合类型,组合类型用来保存所匹配的结果或部分匹配的结果(以便后面能用到),即使遇到有相同的模式。在同样的正则表达中,可能(也通常期望)不止有一个组合类型。在第n个组合类型中匹配结果可以通过“后向引用”来获取到。“后向引用”使得搜索重复的单词非常简单:\b(\w+)\s+\1\b

圆括号形成了一个组合类型,在这个正则表示中它是第一组合类型(也是仅有的一个)。向后扫描\1,指的是任何被\w+所匹配的单词。我们的正则表达式因此能匹配这样的单词,它有一个或多个空格符,后面还跟有一个与此相同的单词。注意的是,尾部的定位类型(\b)必不可少,它可以防止发生错误。如果我们想匹配"Paris in the the spring",而不是匹配"Java's regex package is the theme of this article"。根据Java现在的格式,则上面的正则表达式就是:

Pattern pattern = Pattern.compile("\\b(\\w+)\\s+\\1\\b");

 

最后进一步的修改是让我们的匹配器对大小写敏感。比如,下面的情况:"The the theme of this article is the Java's regex package.",这一点在regex中能非常简单地实现,即通过使用在Pattern类中预定义的静态标志 CASE_INSENSITIVE

Pattern pattern = Pattern.compile("\\b(\\w+)\\s+\\1\\b", Pattern.CASE_INSENSITIVE);

有关正则表达式的话题是非常丰富,而且复杂的,用Java来实现也非常广泛,则需要对regex包进行的彻底研究,我们在这里所讲的只是冰山一角。即使你对正则表达式比较陌生,使用regex包后会很快发现它强大功能和可伸缩性。如果你是个来自Perl或其他语言王国的老练的正则表达式的黑客,使用过regex包后,你将会安心地投入到Java的世界,而放弃其他的工具,并把Java的regex包看成是手边必备的利器。

 



CharSequence

JDK 1.4定义了一个新的接口,叫CharSequence。它提供了String和StringBuffer这两个类的字符序列的抽象:

interface CharSequence {
    charAt(int i);
    length();
    subSequence(int start, int end);
    toString();
}

为了实现这个新的CharSequence接口,String,StringBuffer以及CharBuffer都作了修改。很多正则表达式的操作都要拿CharSequence作参数。

Pattern 和 Matcher

先给一个例子。下面这段程序可以测试正则表达式是否匹配字符串。第一个参数是要匹配的字符串,后面是正则表达式。正则表达式可以有多个。在Unix/linux环境下,命令行下的正则表达式还必须用引号。

// c12:TestRegularExpression.java
// Allows you to easly try out regular expressions.
// {Args: abcabcabcdefabc "abc+" "(abc)+" "(abc){2,}" }
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TestRegularExpression {
    public static void main(String[] args) {
        if (args.length < 2) {
            System.out.println("Usage:\n" +
                    "java TestRegularExpression " +
                    "characterSequence regularExpression+");
            System.exit(0);
        }

        System.out.println("Input: \"" + args[0] + "\"");

        for (int i = 1; i < args.length; i++) {
            System.out.println("Regular expression: \"" + args[i] + "\"");
            Pattern p = Pattern.compile(args[i]);
            Matcher m = p.matcher(args[0]);
            while (m.find()) {
                System.out.println("Match \"" + m.group() +
                        "\"at positions " +
                        m.start() + "-" + (m.end() - 1));
            }
        }
    }
} 

编译和运行:  (相关文章:如何 编译 和 运行 单个 Java 文件? javac & java

$ java TestRegularExpression  wenjianbao   wen jian   bao
Input: "wenjianbao"
Regular expression: "wen"
Match "wen"at positions 0-2
Regular expression: "jian"
Match "jian"at positions 3-6
Regular expression: "bao"
Match "bao"at positions 7-9

Java的正则表达式是由java.util.regex的Pattern和Matcher类实现的

Pattern对象表示经编译的正则表达式。静态的compile()方法负责将表示正则表达式的字符串编译成Pattern对象。正如上述例程所示的,只要给Pattern的matcher()方法送一个字符串就能获取一个Matcher对象。此外,Pattern还有一个能快速判断能否在input里面找到regex的静态方法:

public static boolean matches(String regex, CharSequence input)

以及能返回String数组的split()方法,它能用regex把字符串分割开来。

只要给Pattern.matcher()方法传一个字符串就能获得 Matcher对象 了。接下来就能用Matcher的方法来查询匹配的结果了。

boolean matches()
boolean lookingAt()
boolean find()
boolean find(int start)

matches() 的前提是Pattern匹配整个字符串,而lookingAt()的意思是Pattern匹配字符串的开头。

find()

Matcher.find()的功能是发现CharSequence里的,与pattern相匹配的多个字符序列。例如:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class FindDemo {
    public static void main(String[] args) {
        Matcher m = Pattern.compile("\\w+")
                .matcher("Evening is full of the linnet's wings");
        while (m.find()) {
            System.out.println(m.group());
        }

        int i = 0;
        while (m.find(i)) {
            System.out.print(m.group() + " ");
            i++;
        }
    }
}

输出:

Evening
is
full
of
the
linnet
s
wings
Evening vening ening ning ing ng g is is s full full ull ll l of of f the the he e linnet linnet innet nnet net et t s s wings wings ings ngs gs s 
  • "\\w+"的意思是“一个或多个单词字符”,因此它会将字符串直接分解成单词。
  • find()像一个迭代器,从头到尾扫描一遍字符串。
  • 第二个find()是带int参数的,正如你所看到的,它会告诉方法从哪里开始找 —— 即从参数位置开始查找。

Groups

Group是指里用括号括起来的,能被后面的表达式调用的正则表达式。Group 0 表示整个表达式,group 1表示第一个被括起来的group,以此类推。所以:

A(B(C))D

里面有三个group:group 0是ABCD, group 1是BC,group 2是C。

你可以用下述Matcher方法来使用group:

  • public int groupCount():返回matcher对象中的group的数目。不包括group0
  • public String group():返回上次匹配操作(比方说find())的group 0(整个匹配);
  • public String group(int i):返回上次匹配操作的某个group。如果匹配成功,但是没能找到group,则返回null;
  • public int start(int group):返回上次匹配所找到的group的开始位置;
  • public int end(int group):返回上次匹配所找到的group的结束位置,最后一个字符的下标+1
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Groups {
    // 一首 诗
    private static final String poem =
            "Twas brillig, and the slithy toves\n" +
                    "Did gyre and gimble in the wabe.\n" +
                    "All mimsy were the borogoves,\n" +
                    "And the mome raths outgrabe.\n\n" +
                    "Beware the Jabberwock, my son,\n" +
                    "The jaws that bite, the claws that catch.\n" +
                    "Beware the Jubjub bird, and shun\n" +
                    "The frumious Bandersnatch.";

    public static void main(String[] args) {
        Matcher m = Pattern.compile("(?m)(\\S+)\\s+((\\S+)\\s+(\\S+))$").matcher(poem);
        while (m.find()) {
            for (int j = 0; j <= m.groupCount(); j++) {
                System.out.print("[" + m.group(j) + "]");
            }
            System.out.println();
        }
    }
}

输出:

[Twas brillig, and][Twas][brillig, and][brillig,][and]
[the slithy toves][the][slithy toves][slithy][toves]
[Did gyre and][Did][gyre and][gyre][and]
[gimble in the][gimble][in the][in][the]
[wabe.
All mimsy][wabe.][All mimsy][All][mimsy]
[were the borogoves,][were][the borogoves,][the][borogoves,]
[And the mome][And][the mome][the][mome]
[raths outgrabe.

Beware][raths][outgrabe.

Beware][outgrabe.][Beware]
[the Jabberwock, my][the][Jabberwock, my][Jabberwock,][my]
[son,
The jaws][son,][The jaws][The][jaws]
[that bite, the][that][bite, the][bite,][the]
[claws that catch.][claws][that catch.][that][catch.]
[Beware the Jubjub][Beware][the Jubjub][the][Jubjub]
[bird, and shun][bird,][and shun][and][shun]
[The frumious Bandersnatch.][The][frumious Bandersnatch.][frumious][Bandersnatch.]

这首诗是 Through the Looking Glass 的,Lewis Carroll的"Jabberwocky"的第一部分。可以看到这个正则表达式里有很多用括号括起来的group,它是由任意多个连续的非空字符 (\S+) 和任意多个连续的空格字符 (\s+) 所组成的,其最终目的是要捕获每行的最后三个单词;$ 表示一行的结尾。但是 $ 通常表示整个字符串的结尾,所以这里要明确地告诉正则表达式注意换行符。这一点是由 (?m) 标志完成的(模式标志会过一会儿讲解)。

start() 和 end()

如果匹配成功,start()会返回此次匹配的开始位置,end()会返回此次匹配的结束位置,即最后一个字符的下标+1。如果之前的匹配不成功(或者没匹配),那么无论是调用start()还是end(),都会引发一个IllegalStateException。下面这段程序还演示了matches()lookingAt()

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StartEnd {
    public static void main(String[] args) {
        String[] input = new String[]{
                "Java has regular expressions in 1.4",
                "regular expressions now expressing in Java",
                "Java represses oracular expressions"
        };

        Pattern
                p1 = Pattern.compile("re\\w*"),
                p2 = Pattern.compile("Java.*");

        for (int i = 0; i < input.length; i++) {
            System.out.println("");
            System.out.println("input " + i + ": " + input[i]);
            Matcher
                    m1 = p1.matcher(input[i]),
                    m2 = p2.matcher(input[i]);

            while (m1.find()) {
                System.out.println("m1.find() '" + m1.group() +
                        "' start = " + m1.start() + " end = " + m1.end());
            }

            while (m2.find()) {
                System.out.println("m2.find() '" + m2.group() +
                        "' start = " + m2.start() + " end = " + m2.end());
            }

            if (m1.lookingAt()) { // No reset() necessary
                System.out.println("m1.lookingAt() start = "
                        + m1.start() + " end = " + m1.end());
            }

            if (m2.lookingAt()) {
                System.out.println("m2.lookingAt() start = "
                        + m2.start() + " end = " + m2.end());
            }

            if (m1.matches()) { // No reset() necessary
                System.out.println("m1.matches() start = "
                        + m1.start() + " end = " + m1.end());
            }

            if (m2.matches()) {
                System.out.println("m2.matches() start = "
                        + m2.start() + " end = " + m2.end());
            }
        }
    }
}

输出:

input 0: Java has regular expressions in 1.4
m1.find() 'regular' start = 9 end = 16
m1.find() 'ressions' start = 20 end = 28
m2.find() 'Java has regular expressions in 1.4' start = 0 end = 35
m2.lookingAt() start = 0 end = 35
m2.matches() start = 0 end = 35

input 1: regular expressions now expressing in Java
m1.find() 'regular' start = 0 end = 7
m1.find() 'ressions' start = 11 end = 19
m1.find() 'ressing' start = 27 end = 34
m2.find() 'Java' start = 38 end = 42
m1.lookingAt() start = 0 end = 7

input 2: Java represses oracular expressions
m1.find() 'represses' start = 5 end = 14
m1.find() 'ressions' start = 27 end = 35
m2.find() 'Java represses oracular expressions' start = 0 end = 35
m2.lookingAt() start = 0 end = 35
m2.matches() start = 0 end = 35

注意:只要字符串里有这个模式,find()就能把它给找出来,但是lookingAt()matches(),只有在字符串与正则表达式一开始就相匹配的情况下才能返回true。

  • matches():成功的前提是,正则表达式与字符串完全匹配;
  • lookingAt():成功的前提是,字符串的开始部分与正则表达式相匹配;

匹配的模式(Pattern flags)

compile() 方法还有一个版本,它需要一个控制正则表达式的匹配行为的参数:

Pattern Pattern.compile(String regex, int flag)

flag的取值范围如下:

编译标志 效果
Pattern.CANON_EQ 当且仅当两个字符的"正规分解(canonical decomposition)"都完全相同的情况下,才认定匹配。比如用了这个标志之后,表达式"a\u030A"会匹配"?"。默认情况下,不考虑"规范相等性(canonical equivalence)"。
Pattern.CASE_INSENSITIVE
(?i)
默认情况下,大小写不明感的匹配只适用于US-ASCII字符集。这个标志能让表达式忽略大小写进行匹配。要想对Unicode字符进行大小不明感的匹配,只要将UNICODE_CASE与这个标志合起来就行了。(相关文章:Java正则表达式,如何忽略大小写
Pattern.COMMENTS
(?x)
在这种模式下,匹配时会忽略(正则表达式里的)空格字符(注:不是指表达式里的"\\s",而是指表达式里的空格、tab、回车之类)。注释从#开始,一直到这行结束。可以通过嵌入式的标志来启用Unix行模式。
Pattern.DOTALL
(?s)
在这种模式下,表达式'.'可以匹配任意字符,包括表示一行的结束符。默认情况下,表达式'.'不匹配行的结束符。
(行结束符:Windows下 \r\n,Linux下 \n,Mac下 \r
Pattern.MULTILINE
(?m)
在这种模式下,'^'和'$'分别一行一行地匹配一行的开始和结束。此外,'^'仍然匹配一行字符串的开始,'$'也匹配一行字符串的结束。默认情况下,这两个表达式仅仅匹配字符串的开始和结束。
Pattern.UNICODE_CASE
(?u)
在这个模式下,如果你还启用了CASE_INSENSITIVE标志,那么它会对Unicode字符进行大小写不明感的匹配。默认情况下,大小写不明感的匹配只适用于US-ASCII字符集。
Pattern.UNIX_LINES
(?d)
在这个模式下,只有'\n'才被认作一行的中止,并且与'.','^',以及'$'进行匹配。

在这些标志里面,Pattern.CASE_INSENSITIVEPattern.MULTILINE,以及Pattern.COMMENTS是最有用的(其中Pattern.COMMENTS还能帮我们把思路理清楚,并且\或者做文档)。

注意:你可以用在表达式里插记号的方式来启用绝大多数的模式。这些记号就在上面那张表的各个标志的下面。你希望模式从哪里开始启动,就在哪里插记号。

可以用 "OR" ('|') 运算符把这些标志组合使用:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ReFlags {
    public static void main(String[] args) {
        Pattern p = Pattern.compile("^java",
                Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
        Matcher m = p.matcher(
                "java has regex\nJava has regex\n" +
                        "JAVA has pretty good regular expressions\n" +
                        "Regular expressions are in Java");
        while (m.find()) {
            System.out.println(m.group());
        }
    }
}

输出:

java
Java
JAVA

这样创建出来的正则表达式就能匹配以"java","Java","JAVA"...开头的字符串了。此外,如果字符串分好几行,那它还会对每一行做匹配(匹配始于字符序列的开始,终于字符序列当中的行结束符)。

注意:group()方法仅返回匹配的部分。

split()

所谓分割是指将以正则表达式为界,将字符串分割成String数组。

String[] split(CharSequence charseq)
String[] split(CharSequence charseq, int limit)

这是一种既快又方便地将文本根据一些常见的边界标志分割开来的方法。

import java.util.Arrays;
import java.util.regex.Pattern;

public class SplitDemo {
    public static void main(String[] args) {
        String input =
                "This!!unusual use!!of exclamation!!points";

        System.out.println(Arrays.asList(
                Pattern.compile("!!").split(input)));

        // Only do the first three:
        System.out.println(Arrays.asList(
                Pattern.compile("!!").split(input, 3)));

        System.out.println(Arrays.asList(
                "Aha! String has a split() built in!" .split(" ")));
    }
}

第二个split()会限定分割的次数。

正则表达式 是如此重要,以至于有些功能被加进了String类,其中包括split()(已经看到了),matches()replaceFirst()以及replaceAll()。这些方法的功能同PatternMatcher的相同。

替换操作

正则表达式在替换文本方面特别在行。下面就是一些方法:

  • replaceFirst(String replacement):将字符串里,第一个与模式相匹配的子串替换成replacement。
  • replaceAll(String replacement):将输入字符串里所有与模式相匹配的子串全部替换成replacement。
  • appendReplacement(StringBuffer sbuf, String replacement):对sbuf进行逐次替换,而不是像replaceFirst()或replaceAll()那样,只替换第一个或全部子串。这是个非常重要的方法,因为它可以调用方法来生成replacement(replaceFirst()和replaceAll()只允许用固定的字符串来充当replacement)。
    有了这个方法,你就可以编程区分group,从而实现更强大的替换功能。
    调用完appendReplacement()之后,为了把剩余的字符串拷贝回去,必须调用appendTail(StringBuffer sbuf, String replacement)。
    相关文章:Java 正则表达式 appendReplacement() 和 appendTail() 的用法

下面我们来演示一下怎样使用这些替换方法。

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TheReplacements {
    public static void main(String[] args) throws Exception {
        String s = "/*! Here's a block of text to use as input to\n" +
                "    the regular    expression matcher. Note that we'll\n" +
                "    first extract the block of text by looking for\n" +
                "    the special delimiters, then process the\n" +
                "    extracted block. !*/";

        // Match the specially-commented block of text above:
        Matcher mInput =
                Pattern.compile("/\\*!(.*)!\\*/", Pattern.DOTALL)
                        .matcher(s);

        if (mInput.find()) {
            s = mInput.group(1); // Captured by parentheses(圆括号)
        }

        // Replace two or more spaces with a single space:
        s = s.replaceAll(" {2,}", " ");

        // Replace one or more spaces at the beginning of each
        // line with no spaces. Must enable MULTILINE mode:
        s = s.replaceAll("(?m)^ +", "");

        System.out.println(s);
        System.out.println("");

        s = s.replaceFirst("[aeiou]", "(VOWEL1)");

        System.out.println(s);
        System.out.println("");

        StringBuffer sbuf = new StringBuffer();
        Pattern      p    = Pattern.compile("[aeiou]");
        Matcher      m    = p.matcher(s);

        // Process the find information as you
        // perform the replacements:
        while (m.find()) {
            m.appendReplacement(sbuf, m.group().toUpperCase());
        }

        // Put in the remainder of the text:
        m.appendTail(sbuf);
        System.out.println(sbuf);
    }
}

输出:

Here's a block of text to use as input to
the regular expression matcher. Note that we'll
first extract the block of text by looking for
the special delimiters, then process the
extracted block. 

H(VOWEL1)re's a block of text to use as input to
the regular expression matcher. Note that we'll
first extract the block of text by looking for
the special delimiters, then process the
extracted block. 

H(VOWEL1)rE's A blOck Of tExt tO UsE As InpUt tO
thE rEgUlAr ExprEssIOn mAtchEr. NOtE thAt wE'll
fIrst ExtrAct thE blOck Of tExt by lOOkIng fOr
thE spEcIAl dElImItErs, thEn prOcEss thE
ExtrActEd blOck. 

我们将所有两个以上的连续空格全都替换成一个,并且将各行开头的空格全都去掉(为了让这个正则表达式能对所有的行,而不仅仅是第一行起作用,必须启用多行模式)。这两个操作都用了String的replaceAll()(这里用它更方便)。注意,由于每个替换只做一次,因此除了预编译Pattern之外,程序没有额外的开销。

replaceFirst()只替换第一个子串。此外,replaceFirst()replaceAll()只能用常量(literal)来替换,所以如果每次替换的时候还要进行一些操作的话,它们是无能为力的。碰到这种情况,得用appendReplacement(),它能在进行替换的时候想写多少代码就写多少。在上面那段程序里,创建sbuf的过程就是选group做处理,也就是用正则表达式把元音字母找出来,然后换成大写的过程。通常你得在完成全部的替换之后才调用appendTail(),但是如果要模仿replaceFirst()(或"replace n")的效果,你也可以只替换一次就调用appendTail()。它会把剩下的东西全都放进sbuf。

你还可以在appendReplacement()的replacement参数里用"$g"引用已捕获的group,其中'g' 表示group的号码(即 0、1、2、3 等数字序号)。不过这是为一些比较简单的操作准备的,因而其效果无法与上述程序相比。

reset()

此外,还可以用reset()方法给现有的Matcher对象配上个新的CharSequence。如果不给参数,reset()会把Matcher设到当前字符串的开始处

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Resetting {
    public static void main(String[] args) {
        Matcher m = Pattern.compile("[frb][aiu][gx]")
                .matcher("fix the rug with bags");

        while (m.find()) {
            System.out.println(m.group());
        }

        System.out.println("");

        m.reset("fix the rig with rags");

        while (m.find()) {
            System.out.println(m.group());
        }
    }
}

输出:

fix
rug
bag

fix
rig
rag

 

 

相关文章:

如何 编译 和 运行 单个 Java 文件? javac & java

Java正则表达式,如何忽略大小写

Java 正则表达式 appendReplacement() 和 appendTail() 的用法

Java 正则表达式,后向引用($1, $2 ...)