正则表达式是用字符串描述的一个匹配规则,使用正则表达式可以快速判断给定的字符串是否符合匹配规则。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,即整个正则匹配到的字符串。
public class Main {
public static void main(String[] args) {
Pattern p = Pattern.compile("(\\d{3,4})\\-(\\d{7,8})");
Matcher m = p.matcher("010-12345678");
if (m.matches()) {
String g1 = m.group(1);
String g2 = m.group(2);
System.out.println(g1);
System.out.println(g2);
} else {
System.out.println("匹配失败!");
}
}
}
正则表达式匹配默认使用贪婪匹配,可以使用?表示对某一规则进行非贪婪匹配。
注意区分?的含义:\d??。
我们再来看这个正则表达式(\d??)(9*),注意\d?表示匹配0个或1个数字,后面第二个?表示非贪婪匹配,因此,给定字符串”9999”,匹配到的两个子串分别是””和”9999”,因为对于\d?来说,可以匹配1个9,也可以匹配0个9,但是因为后面的?表示非贪婪匹配,它就会尽可能少的匹配,结果是匹配了0个9。
4.搜索和替换
1.分割字符串
使用正则表达式分割字符串可以实现更加灵活的功能。String.split()方法传入的正是正则表达式。我们来看下面的代码:
"a b c".split("\\s"); // { "a", "b", "c" }
"a b c".split("\\s"); // { "a", "b", "", "c" }
"a, b ;; c".split("[\\,\\;\\s]+"); // { "a", "b", "c" }
2.搜索字符串
public class Main {
public static void main(String[] args) {
String s = "the quick brown fox jumps over the lazy dog.";
Pattern p = Pattern.compile("\\wo\\w");
Matcher m = p.matcher(s);
while (m.find()) {
String sub = s.substring(m.start(), m.end());
System.out.println(sub);
}
}
}
3.替换字符串
使用正则表达式替换字符串可以直接调用String.replaceAll(),它的第一个参数是正则表达式,第二个参数是待替换的字符串。我们还是来看例子:
public class Main {
public static void main(String[] args) {
String s = "The quick\t\t brown fox jumps over the lazy dog.";
String r = s.replaceAll("\\s+", " ");
System.out.println(r); // "The quick brown fox jumps over the lazy dog."
}
}
4.反向引用
如果我们要把搜索到的指定字符串按规则替换,比如前后各加一个xxxx,这个时候,使用replaceAll()的时候,我们传入的第二个参数可以使用$1、$2来反向引用匹配到的子串。例如:
public class Main {
public static void main(String[] args) {
String s = "the quick brown fox jumps over the lazy dog.";
String r = s.replaceAll("\\s([a-z]{4})\\s", " <b>$1</b> ");
System.out.println(r);
}
}
使用正则表达式可以:
- 分割字符串:String.split()
- 搜索子串:Matcher.find()
- 替换字符串:String.replaceAll()