正则表达式是用字符串描述的一个匹配规则,使用正则表达式可以快速判断给定的字符串是否符合匹配规则。Java标准库java.util.regex内建了正则表达式引擎。

1.匹配规则

要注意正则表达式在Java代码中也是一个字符串,所以,对于正则表达式a\&c来说,对应的Java字符串是”a\&c”,因为\也是Java字符串的转义字符,两个\实际上表示的是一个\。

1.匹配任意字符

我们可以用.匹配一个任意字符。

2.匹配数字

想匹配0~9这样的数字,可以用\d匹配。\d仅限单个数字字符。

3.匹配常用字符

用\w可以匹配一个字母、数字或下划线,w的意思是word。\w不能匹配#、空格等字符。

4.匹配空格字符

用\s可以匹配一个空格字符,注意空格字符不但包括空格,还包括tab字符(在Java中用\t表示)。

5.匹配非数字

用\d可以匹配一个数字,而\D则匹配一个非数字。
\W可以匹配\w不能匹配的字符,\S可以匹配\s不能匹配的字符,这几个正好是反着来的。

6.重复匹配

修饰符*可以匹配任意个字符,包括0个字符。
修饰符+可以匹配至少一个字符。
修饰符?可以匹配0个或一个字符。
想指定匹配n~m个字符怎么办?用修饰符{n,m}就可以。
单个字符的匹配规则如下:

正则表达式 规则 可以匹配
A 指定字符 A
\u548c 指定Unicode字符
. 任意字符 a,b,&,0
\d 数字0~9 0~9
\w 大小写字母,数字和下划线 a~z,A~Z,0~9,_
\s 空格、Tab键 空格,Tab
\D 非数字 a,A,&,_,……
\W 非\w &,@,中,……
\S 非\s a,A,&,_,……

多个字符的匹配规则如下:

正则表达式 规则 可以匹配
A* 任意个数字符 空,A,AA,AAA,……
A+ 至少1个字符 A,AA,AAA,……
A? 0个或1个字符 空,A
A{3} 指定个数字符 AAA
A{2,3} 指定范围个数字符 AA,AAA
A{2,} 至少n个字符 AA,AAA,AAAA,……
A{0,3} 最多n个字符 空,A,AA,AAA

2.复杂匹配规则

1.匹配开头和结尾

用正则表达式进行多行匹配时,我们用^表示开头,$表示结尾。

2.匹配指定范围

使用[…]可以匹配范围内的字符
[…]还有一种写法,[123456789]直接写[1-9]就可以。
要匹配大小写不限的十六进制数,
可以这样写:[0-9a-fA-F],它表示一共可以匹配以下任意范围的字符:

  • 0-9:字符0~9;
  • a-f:字符a~f;
  • A-F:字符A~F。

如果要匹配6位十六进制数,前面讲过的{n}仍然可以继续配合使用:[0-9a-fA-F]{6}。
[…]还有一种排除法,即不包含指定范围的字符。假设我们要匹配任意字符,但不包括数字,
可以写[^1-9]{3}

3.或规则匹配

用|连接的两个正则规则是规则,例如,AB|CD表示可以匹配AB或CD。

4.使用括号

现在我们想要匹配字符串learn java、learn php和learn go怎么办?一个最简单的规则是learn\sjava|learn\sphp|learn\sgo,但是这个规则太复杂了,可以把公共部分提出来,然后用(…)把子规则括起来表示成learn\s(java|php|go)。
复杂匹配规则主要有:

正则表达式 规则 可以匹配
^ 开头 字符串开头
$ 结尾 字符串结束
[ABC] […]内任意字符 A,B,C
[A-F0-9xy] 指定范围的字符 A,……,F,0,……,9,x,y
[^A-F] 指定范围外的任意字符 非A~F
AB|CD|EF AB或CD或EF AB,CD,EF

3.分组匹配

正则表达式用(…)分组可以通过Matcher对象快速提取子串:

  • group(0)表示匹配的整个字符串;
  • group(1)表示第1个子串,group(2)表示第2个子串,以此类推。

要特别注意,Matcher.group(index)方法的参数用1表示第一个子串,2表示第二个子串。如果我们传入0会得到什么呢?答案是010-12345678,即整个正则匹配到的字符串。

  1. public class Main {
  2. public static void main(String[] args) {
  3. Pattern p = Pattern.compile("(\\d{3,4})\\-(\\d{7,8})");
  4. Matcher m = p.matcher("010-12345678");
  5. if (m.matches()) {
  6. String g1 = m.group(1);
  7. String g2 = m.group(2);
  8. System.out.println(g1);
  9. System.out.println(g2);
  10. } else {
  11. System.out.println("匹配失败!");
  12. }
  13. }
  14. }

正则表达式匹配默认使用贪婪匹配,可以使用?表示对某一规则进行非贪婪匹配。
注意区分?的含义:\d??。
我们再来看这个正则表达式(\d??)(9*),注意\d?表示匹配0个或1个数字,后面第二个?表示非贪婪匹配,因此,给定字符串”9999”,匹配到的两个子串分别是””和”9999”,因为对于\d?来说,可以匹配1个9,也可以匹配0个9,但是因为后面的?表示非贪婪匹配,它就会尽可能少的匹配,结果是匹配了0个9。

4.搜索和替换

1.分割字符串

使用正则表达式分割字符串可以实现更加灵活的功能。String.split()方法传入的正是正则表达式。我们来看下面的代码:

  1. "a b c".split("\\s"); // { "a", "b", "c" }
  2. "a b c".split("\\s"); // { "a", "b", "", "c" }
  3. "a, b ;; c".split("[\\,\\;\\s]+"); // { "a", "b", "c" }

2.搜索字符串

  1. public class Main {
  2. public static void main(String[] args) {
  3. String s = "the quick brown fox jumps over the lazy dog.";
  4. Pattern p = Pattern.compile("\\wo\\w");
  5. Matcher m = p.matcher(s);
  6. while (m.find()) {
  7. String sub = s.substring(m.start(), m.end());
  8. System.out.println(sub);
  9. }
  10. }
  11. }

3.替换字符串

使用正则表达式替换字符串可以直接调用String.replaceAll(),它的第一个参数是正则表达式,第二个参数是待替换的字符串。我们还是来看例子:

  1. public class Main {
  2. public static void main(String[] args) {
  3. String s = "The quick\t\t brown fox jumps over the lazy dog.";
  4. String r = s.replaceAll("\\s+", " ");
  5. System.out.println(r); // "The quick brown fox jumps over the lazy dog."
  6. }
  7. }

4.反向引用

如果我们要把搜索到的指定字符串按规则替换,比如前后各加一个xxxx,这个时候,使用replaceAll()的时候,我们传入的第二个参数可以使用$1、$2来反向引用匹配到的子串。例如:

  1. public class Main {
  2. public static void main(String[] args) {
  3. String s = "the quick brown fox jumps over the lazy dog.";
  4. String r = s.replaceAll("\\s([a-z]{4})\\s", " <b>$1</b> ");
  5. System.out.println(r);
  6. }
  7. }

使用正则表达式可以:

  • 分割字符串:String.split()
  • 搜索子串:Matcher.find()
  • 替换字符串:String.replaceAll()