appendReplacement和appendTail函数用法

appendReplacement和appendTail

此示例来自Thinking in Java.4e。很经典,包含了很多知识点,包括正则相关。

import java.util.regex.*;

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);
        s = s.replaceFirst("[aeiou]", "(VOWEL1)");
        
        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);
    }
} /* Output:
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.
*///:~
  • Pattern.compile(正则)方法会返回一个Pattern对象,这个对象只是关于正则表达式的。而Pattern对象.matcher(String字符串)会返回一个Matcher对象,这个对象是关于Pattern对象存的正则和特定字符串。
  • "/\\*!(.*)!\\*/"这个正则\\转义为\,然后\*转义为普通字符*,这样就能匹配到字符串s的开头和结束了。(.*)里,.表示任意字符,*表示零个或多个。Pattern.DOTALL这个常量使得.还能匹配到换行符,默认不匹配。
  • 正则表达式有分组概念。整个正则给与默认组号0,而其余组号从(开始分配,第1组当然是第一个(和与其对应的)之间的正则内容。mInput.group(1)如果没有给参数,那么匹配的是0分组即整个正则;而这里给了参数1,则是从整个正则匹配到的内容里面抽取处1分组的内容,而1分组的正则为(.*)
  • " {2,}"表示,两个或以上的空格。"(?m)^ +",首先在(?m)这种模式下,^$还分别匹配一行的开始和结束(此外,^仍然匹配字符串的开始,$也匹配字符串的结束。默认情况下,这两个表达式仅仅匹配字符串的开始和结束);+表示一个或多个空格。
  • 从上面可以看到replaceFirstreplaceAll两个函数限制很大,要么替换第一个匹配内容,要么替换所有匹配内容。
  • 每当m.find()函数运行成功后(返回真),m.group()函数就能上一次匹配到的字符串返回出来。(感觉就像迭代器,用了hasNext后,就可以用next)
  • 这是函数签名public Matcher appendReplacement(StringBuffer sb, String replacement)。现在被替换的字符串知道了,要匹配的正则也知道了,就差匹配后用来替换的字符串了,就是这个replacement参数。
        while(m.find())
            m.appendReplacement(sbuf, m.group().toUpperCase());
        m.appendTail(sbuf);

上面这个循环就是appendReplacement和appendTail函数的一般用法,就是这样在循环里使用。主要是每次m.find()后,再对每次匹配到的内容进行替换即m.appendReplacement()

这里重点讲一下appendReplacement的灵活之处。

  1. 在循环中,你可以选择某次循环不作处理。
  2. 在各次循环中,可以让replacement参数不同。也可以让StringBuffer对象不同。
  3. replacement参数可以获得正则里的分组,比如可以m.appendReplacement(sbuf, "$0ttt")(效果就是$0 => $0ttt),由于0分组是整个正则,那么这里的效果是在匹配到的内容后加上ttt。
  4. 在你做了应该做的替换们后(可能后面还可以替换),直接用appendTail函数就可以把后面没检测过的字符串全加上去。
  5. 当然,循环不是必须的。你可以手动多写几次m.find()

举一反三的:

  1. 一次appendReplacement()都不执行,直接执行m.appendTail(),可以把字符串s原封不动地加到StringBuffer里。
  2. 执行一次appendReplacement(),然后执行m.appendTail(),则效果和replaceFirst的效果一样。
  3. 不管是appendReplacement(),还是m.appendTail(),都需要指定StringBuffer对象,这意味着你每次执行append时可以选择加到不同的StringBuffer对象。

顺便再看看appendReplacement()函数的api文档吧:

This method performs the following actions:
1.It reads characters from the input sequence, starting at the append position, and appends them to the given string buffer. It stops after reading the last character preceding the previous match, that is, the character at index start() - 1.
2.It appends the given replacement string to the string buffer.
3.It sets the append position of this matcher to the index of the last character matched, plus one, that is, to end().

  1. 它会从特定字符串读取字符,起码位置是append position,终止位置为上一次匹配的第一个字符的之前那个字符(指上一次find()),然后把这些字符append到StringBuffer上去。上一次匹配的第一个字符的位置可通过start()返回,所以终止位置就是start()-1了。
  2. 把replacement参数append到StringBuffer上去。
  3. 把append position设置为上一次匹配的最后字符位置加1,end()函数也能返回这个位置。即end()函数返回append position。
  4. 个人总结:每次成功匹配后(上一次find()返回真),start()返回上一次匹配的第一个字符的位置,end()返回上一次匹配的最后字符的下一个位置。而append position这个位置与appendReplacement()函数的执行时机有关,由于此函数分为了以上三个步骤,所以:在第1步时,append position为执行appendReplacement()函数的上上次find()end()返回值;在第3步执行完后,append position为执行appendReplacement()函数的上一次find()end()返回值。

还有appendTail()函数的api文档:

This method reads characters from the input sequence, starting at the append position, and appends them to the given string buffer. It is intended to be invoked after one or more invocations of the appendReplacement method in order to copy the remainder of the input sequence.

它会从特定字符串读取字符,起码位置是append position,终止位置它没说(应该是指sequence的最后一个字符),然后把这些字符append到StringBuffer上去。它旨在在appendReplacement方法的一次或多次调用之后调用,以便复制input sequence的其余部分。

start() end()

这两个函数在从未执行过find(),或者上一次find()返回假时,会抛出异常IllegalStateException
这两个函数还分别重载了两个版本:(int group)(String name),分别以正则表达式里面的分组的组号或组名来返回位置。

发布了171 篇原创文章 · 获赞 130 · 访问量 28万+

猜你喜欢

转载自blog.csdn.net/anlian523/article/details/100128884