Pattern和Matcher

构建正则表达式对象:

  • 导入java.util.regex包
  • 用static Pattern.compile()方法编译正则表达式
  • 根据String类型的正则表达式生成一个Pattern对象
  • 把将要检索的字符串传入Pattern对象的matcher()方法。matcher()方法会生成一个Matcher对象
  1. import java.util.regex.Matcher;
  2. import java.util.regex.Pattern;
  3. public class Test {
  4. public static void main(String[] args){
  5. String[] str={"abcabcabcdefabc","abc+","(abc)+","(abc){2,}"};
  6. System.out.println("Input:\""+str[0]+"\"");
  7. for(String s : str){
  8. System.out.println("Regular expression:\""+s+"\"");
  9. Pattern p=Pattern.compile(s);
  10. Matcher m = p.matcher(str[0]);
  11. while(m.find()){
  12. System.out.println("Match \""+m.group()+"\" at positions "+m.start()+"-"+(m.end()-1));
  13. }
  14. System.out.println();
  15. }
  16. }
  17. }
  18. /* Output:
  19. Input:"abcabcabcdefabc"
  20. Regular expression:"abcabcabcdefabc"
  21. Match "abcabcabcdefabc" at positions 0-14
  22. Regular expression:"abc+"
  23. Match "abc" at positions 0-2
  24. Match "abc" at positions 3-5
  25. Match "abc" at positions 6-8
  26. Match "abc" at positions 12-14
  27. Regular expression:"(abc)+"
  28. Match "abcabcabc" at positions 0-8
  29. Match "abc" at positions 12-14
  30. Regular expression:"(abc){2,}"
  31. Match "abcabcabc" at positions 0-8
  32. */

Pattern类还提供了static方法:

  1. static boolean metches(String regex,CharSequence input)
  2. // 该方法用以检查regex是否匹配整个CharSequence类型的input参数。

编译后的Pattern对象还提供了split()方法,它从匹配了regex的地方分割字符串,返回分割后的子字符串String数组。
通过调用Pattern.matcher()方法,并传入一个字符串参数,我们得到了一个Matcher对象。使用Matcher上的方法,我们将能够判断各种不同类型的匹配是否成功:

  1. boolean matches() // 用来判断整个输入字符串是否匹配正则表达式模式
  2. boolean lookingAt() // 用来判断该字符串(不必是整个字符串)的始部分是否能够匹配模式
  3. boolean find()
  4. boolean find(int start)

find()

Matcher.find()方法可以用来在CharSequence中查找多个匹配。

  1. import java.util.regex.*;
  2. public class Test {
  3. public static void main(String[] args){
  4. Matcher m = Pattern.compile("\\w+").matcher("Evening is full of the linnet's wings");
  5. while(m.find())
  6. System.out.print(m.group()+" ");
  7. System.out.println();
  8. int i=0;
  9. while (m.find(i)){
  10. System.out.print(m.group()+" ");
  11. i++;
  12. }
  13. }
  14. }
  15. /* Output:
  16. Evening is full of the linnet s wings
  17. 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
  18. */
  19. /* 解析:
  20. 模式\\w+将字符串划分为单词。find()像迭代器那样前向遍历输入字符串。而第二个find()能够接收一个整数作为参数,该整数表示字符串中字符的位置,并以其作为搜索的起店。从结果中可以看出,后一个版本的find()方法能够根据其参数的值,不断重新设定搜索的起始位置。
  21. */

组(Groups)

组是用括号划分的正则表达式,可以根据组的编号来引用某个组。组号为0表示整个表达式,组好1表示被第一对括号括起的组,依此类推。
因此,A(B(C))D这个表达式中有三个组:组0是ABCD,组1是BC,组2是C。

Matcher对象提供了一系列方法,用以获取与组相关的信息:

  1. public int groupCount() // 返回该匹配器的模式中的分组数目,第0组不包括在内。
  2. public String group() // 返回前一次匹配操作(例如find())的第0组(整个匹配)。
  3. public String group(int i) // 返回在前一次匹配操作期间指定的组号,如果匹配成功,但指定的组没有匹配输入字符串的任何部分,则将会返回null
  4. public int start(int group) // 返回在前一次匹配操作中寻找到的组的起始索引。
  5. public int end(int group) // 返回在前一次匹配操作中寻找到的组的最后一个字符索引加一的值。
  1. import java.util.regex.*;
  2. public class Groups {
  3. static public final String POEM=
  4. "Twas brillig, and the slithy toves\n"+
  5. "Did gyre and gimble in the wabe.\n"+
  6. "All mimsy were the borogoves,\n"+
  7. "And the mome raths outgrabe.\n\n"+
  8. "Beware the Jabberwock, my son,\n"+
  9. "The jaws that bite, the claws that catch,\n"+
  10. "Beware the Jubjub bird, and shun\n"+
  11. "The frumious Bandersnatch.";
  12. public static void main(String[] args){
  13. Matcher m = Pattern.compile("(?m)(\\S+)\\s+((\\S+)\\s+(\\S+))").matcher(POEM);
  14. while(m.find()){
  15. for (int j =0;j<=m.groupCount();j++)
  16. System.out.print("["+m.group(j)+"]");
  17. System.out.println();
  18. }
  19. }
  20. }
  21. /* Output:
  22. [the slithy toves][the][slithy toves][slithy][toves]
  23. [in the wabe.][in][the wabe.][the][wabe.]
  24. [were the borogoves,][were][the borogoves,][the][borogoves,]
  25. [mome raths outgrabe.][mome][raths outgrabe.][raths][outgrabe.]
  26. [Jabberwock, my son,][Jabberwock,][my son,][my][son,]
  27. [claws that catch,][claws][that catch,][that][catch,]
  28. [bird, and shun][bird,][and shun][and][shun]
  29. [The frumious Bandersnatch.][The][frumious Bandersnatch.][frumious][Bandersnatch.]
  30. */
  31. /* 解析:
  32. 该正则表达式由任意数目的非空字符(\S+)及随后的任意数目的空格字符(\s+)所组成,目的是捕获每行的最后三个词,每行最后以$结束。不过在正常情况下是将$与整个输入序列的末端相匹配。所以我们一定要显示地告知正则表达式注意输入序列中的换行符。这可以由序列开头的模式标记(?m)来完成
  33. */

start()与end()

在匹配操作成功后,start()返回先前匹配的起始位置的索引,而end()返回所匹配的最后字符的索引加一的值。
匹配操作失败后调用start()或end()将会产生IllegealStateException。

  1. // 该示例还同时展示了matches()和lookingAt()的用法
  2. import java.util.regex.*;
  3. public class StartEnd {
  4. public static String input=
  5. "As long as there is injustice, whenever a\n"+
  6. "Targathian baby cries out, wherever a distress\n"+
  7. "signal sounds among the stars ... we'll be there.\n"+
  8. "This fine ship, and this fine crew ...\n"+
  9. "Never give up! Never surrender!";
  10. private static class Display{
  11. private boolean regexPrinted = false ;
  12. private String regex;
  13. Display(String regex){ this.regex=regex; }
  14. void display(String message){
  15. if(!regexPrinted){
  16. System.out.println(regex);
  17. regexPrinted=true;
  18. }
  19. System.out.println(message);
  20. }
  21. }
  22. static void examine(String s,String regex){
  23. Display d = new Display(regex);
  24. Matcher m = Pattern.compile(regex).matcher(s);
  25. while (m.find()){
  26. d.display("find() '"+m.group()+"' start = "+m.start()+" end = "+m.end());
  27. }
  28. if (m.lookingAt()) // No reset() necessary
  29. d.display("lookingAt() start = "+m.start()+" end = "+m.end());
  30. if (m.matches()) // No reset() necessary
  31. d.display("matches() start = "+m.start()+" end = "+m.end());
  32. }
  33. public static void main(String[] args){
  34. for (String in : input.split("\n")){
  35. System.out.println("input: "+ in);
  36. for (String regex : new String[]{"\\w*ere\\w*","\\w*ever","T\\w+","Never.*?!"})
  37. examine(in,regex);
  38. }
  39. }
  40. }
  41. /* Output:
  42. input: As long as there is injustice, whenever a
  43. \w*ere\w*
  44. find() 'there' start = 11 end = 16
  45. \w*ever
  46. find() 'whenever' start = 31 end = 39
  47. input: Targathian baby cries out, wherever a distress
  48. \w*ere\w*
  49. find() 'wherever' start = 27 end = 35
  50. \w*ever
  51. find() 'wherever' start = 27 end = 35
  52. T\w+
  53. find() 'Targathian' start = 0 end = 10
  54. lookingAt() start = 0 end = 10
  55. input: signal sounds among the stars ... we'll be there.
  56. \w*ere\w*
  57. find() 'there' start = 43 end = 48
  58. input: This fine ship, and this fine crew ...
  59. T\w+
  60. find() 'This' start = 0 end = 4
  61. lookingAt() start = 0 end = 4
  62. input: Never give up! Never surrender!
  63. \w*ever
  64. find() 'Never' start = 0 end = 5
  65. find() 'Never' start = 15 end = 20
  66. lookingAt() start = 0 end = 5
  67. Never.*?!
  68. find() 'Never give up!' start = 0 end = 14
  69. find() 'Never surrender!' start = 15 end = 31
  70. lookingAt() start = 0 end = 14
  71. matches() start = 0 end = 31
  72. */

注意:find()可以在输入的任意位置定位正则表达式,而lookingAt()和matches()只有在正则表达式与输入的最开始处就开始匹配时才会成功。matches()只有在整个输入都匹配正则表达式时才会成功,而lookingAt()只要输入的第一部分匹配就会成功。

Pattern标记

Pattern.compile()方法还有另一个版本,它接受一个标记参数,以调整匹配的行为:

  1. Pattern Pattern.compile(String regex,int flag);

其中的flag来自以下的Pattern类中的常量

编译标记 效果
Pattern.CANON_EQ 两个字符当且仅当它们的完全规范分解想匹配时,就认为它们是匹配的。例如,如果我们指定这个标记,表达式a\u030A就会匹配字符串?。在默认的情况下,匹配不考虑规范的等价性。
Pattern.CASE_INSENSITIVE(?i) 默认情况下,大小写不敏感的匹配假定只有US-ASCII字符集中的字符才能进行。这个标记允许模式匹配不必考虑大小写。通过指定UNICODE_CASE标记及结合此标记,基于Unicode的大小写不敏感的匹配就可以开启了。
Pattern.COMMENTS(?x) 在这种模式下,空格符将被忽略掉,并且以#开始直到行末的注释也会被忽略掉。通过嵌入的标记表达式也可以开启Unix的行模式
Pattern.DOTALL(?s) 在dotall模式中,表达式“.”匹配所有字符,包括行终结符。默认情况下,“.”表达式不匹配行终结符
Pattern.MULTILINE(?m) 在多行模式下,表达式^和$分别匹配一行的开始和结束。^还匹配输入字符串的开始,而$还匹配输入字符串的结尾。默认情况下,这些表达式仅匹配输入的完整字符串的开始和结束
Pattern.UNICODE_CASE(?u) 当指定这个标记,并且开启CASE_INSENSITIVE时,大小写不敏感的匹配将按照与Unicode标准相一致的方式进行。默认情况下,大小写不敏感的匹配假定只能在US-ASCII字符集中的字符才能进行
Pattern.UNIX_LINES(?d) 在这种模式下,在.、^和$行为中,只识别行终结符\n

注意:你可以直接在正则表达式中使用其中的大多数标记,只需要将上表中括号中括起来的字符插入到正则表达式中,你希望它起作用的位置即可。

  1. import java.util.regex.*;
  2. public class ReFlag {
  3. public static void main(String[] args){
  4. Pattern p=Pattern.compile("^java",Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
  5. Matcher m = p.matcher("java has regex\nJava has regex\n"+
  6. "JAVA has pretty good regular expressions\n"+
  7. "Regular expressions are in Java");
  8. while(m.find())
  9. System.out.println(m.group());
  10. }
  11. }
  12. /* Output:
  13. java
  14. Java
  15. JAVA
  16. */

split()

split()方法将输入字符串断开成字符串对象数组,断开边界由下列正则表达式确定:

  1. String[] split(CharSequence input)
  2. String[] split(CharSequence input,int limit) // 可以限制将输入分割成字符串的数量
  1. import java.util.Arrays;
  2. import java.util.regex.*;
  3. public class SplitDemo {
  4. public static void main(String[] args){
  5. String input="This!!unusual use!!of exclamation!!points";
  6. System.out.println(Arrays.toString(Pattern.compile("!!").split(input)));
  7. // Only do the first three
  8. System.out.println(Arrays.toString(Pattern.compile("!!").split(input,3)));
  9. }
  10. }
  11. /* Output:
  12. [This, unusual use, of exclamation, points]
  13. [This, unusual use, of exclamation!!points]
  14. */

替换操作

正则表达式特别便于替换文本,它提供了许多方法:

  1. replaceFirst(String replacement) // 以参数字符串replacement替换掉第一个匹配成功的部分
  2. replaceAll(String replacement) // 以参数字符串replacement替换掉所有匹配成功的部分
  3. appendReplacement(StringBuffer sbuf,String replacement)
  4. // 执行渐进式的替换,它允许你调用其他方法来生成或处理replacement(replaceFirst()和replaceAll()则只能使用一个固定的字符串),使你能够以编程的方式将目标分割成组,从而具备更强大的替换功能。
  5. appendTail(StringBuffer subf) // 在执行了一次或者多次appendReplacement() 后,调用此方法可以将输入字符串余下的部分复制到sbuf中
  1. import java.util.regex.*;
  2. public class TheReplacements {
  3. public static void main(String[] args){
  4. String s="Here's a block of text to use as input to\n" +
  5. "the regular expression matcher.Note that we'll\n" +
  6. "first extract the block of text by looking for\n" +
  7. "the special delimiters, then process the\n" +
  8. "extracted block.\n";
  9. Matcher mInput = Pattern.compile("/\\*!(.*)!\\*/",Pattern.DOTALL).matcher(s);
  10. if (mInput.find())
  11. s=mInput.group(1); // Captured by parentheses
  12. s=s.replaceAll(" {2,} "," "); // Replace 2 or more spaces with a single space
  13. // Replace 1 or more spaces at the beginning of each line with no spaces. Must enable NULTILINE mode.
  14. s=s.replaceAll("(?m)^ +","");
  15. System.out.println(s);
  16. s= s.replaceFirst("[aeiou]","(VOWEL1)");
  17. StringBuffer sbuf =new StringBuffer();
  18. Pattern p =Pattern.compile("[aeiou]");
  19. Matcher m=p.matcher(s);
  20. // Process the find information sa you perform the replacements:
  21. while (m.find())
  22. m.appendReplacement(sbuf,m.group().toUpperCase());
  23. // Put in the remainder of the text
  24. m.appendTail(sbuf);
  25. System.out.println(sbuf);
  26. }
  27. }
  28. /* Output:
  29. Here's a block of text to use as input to
  30. the regular expression matcher.Note that we'll
  31. first extract the block of text by looking for
  32. the special delimiters, then process the
  33. extracted block.
  34. H(VOWEL1)rE's A blOck Of tExt tO UsE As InpUt tO
  35. thE rEgUlAr ExprEssIOn mAtchEr.NOtE thAt wE'll
  36. fIrst ExtrAct thE blOck Of tExt by lOOkIng fOr
  37. thE spEcIAl dElImItErs, thEn prOcEss thE
  38. ExtrActEd blOck.
  39. */
  40. /* 解析:
  41. 这两个替换操作所使用的replaceAll()是String对象自带的方法,在这里,使用此方法更方便。注意,因为这两个替换操作都只使用了一次replaceAll(),所以,与其编译为Pattern,不如直接使用S的replaceAll()方法,而且开销也更小些。
  42. replaceFirst()和replaceAll()方法用来替换的至少普通的字符串,所以,如果想对这些替换字符串执行某些特殊处理,这两个方法是无法胜任的。如果你想要这么做,就应该使用appendReplacement()方法。还方法允许你在执行替换的过程中,操作用来替换的字符串。
  43. 该例子,先构造了sbuf用来保存最终结果,然后用group()选择一个组,并将其进行处理,将正则表达式找到的元音字面转换为大写字母。一般情况下,你应该遍历执行所有的替换操作,然后再调用appendTail()方法,但是,如果你想模拟replaceFirst()(或替换n次)的行为,那就只需执行一次替换,然后调用appendTail()方法,将剩余为处理的部分存入sbuf即可。
  44. */

reset()

通过reset()方法,可以将现有的Matcher对象应用于一个新的字符序列:

  1. import java.util.regex.*;
  2. public class Resetting {
  3. public static void main(String[] args){
  4. Matcher m = Pattern.compile("[frb][aiu][gx]").matcher("fix the rug with bags");
  5. while (m.find())
  6. System.out.print(m.group()+" ");
  7. System.out.println();
  8. m.reset("fix the rig with bags");
  9. while (m.find())
  10. System.out.print(m.group()+" ");
  11. }
  12. }
  13. /* Output :
  14. fix rug bag
  15. fix rig bag
  16. */

使用不带参数的reset()方法,可以将Matcher对象重新设置到当前字符序列的初始位置。