1. 正则表达式简介
正则表达式可以用字符串来描述规则,并用来匹配字符串。
举个例子:要判断用户输入的年份是否是 20## 年。
规则:
2,0,0~9任意数字,0~9任意数字
正则表达式:
20\d\d
Java 字符串(Java 字符串用 \\
表示 \
):
20\\d\\d
代码:
public static boolean is20xx(String str) {
return str.matches("20\\d\\d");
}
2. 正则表达式匹配规则
正则表达式的匹配规则是从左到右按规则匹配。
精确匹配
正则表达式1:
abc
它只能精确地匹配字符串 "abc"
正则表达式2:
a\&c
其中 \&
是用来匹配特殊字符 &
的,它只能精确匹配字符串 "a&c"
匹配任意字符
正则表达式:
a.c
其中 .
可以匹配一个任意字符,"abc"
,"a&c"
,"acc"
都可以匹配
匹配数字
正则表达式:
00\d
其中 \d
可以匹配 0
~9
这样的数字,"007"
,"008"
都可以匹配
匹配常用字符
正则表达式:
java\w
其中 \w
可以匹配一个字母、数字或下划线,"javac"
,"java9"
,"java_"
都可以匹配
匹配空格字符
正则表达式:
a\sc
其中 \s
可以匹配一个空格字符,注意空格字符不但包括空格,还包括 tab 字符
"a c"
,"a\tc"
都可以匹配
匹配非数字
正则表达式:
00\D
其中 \D
可以匹配一个非数字,"00A"
,"00#"
都可以匹配,但 "007"
不能匹配
类似的,\W
可以匹配 \w
不能匹配的字符,\S
可以匹配 \s
不能匹配的字符,这几个正好是反着来的
匹配任意个字符
正则表达式:
A\d*
其中 *
可以匹配任意个字符,"A"
,"A0"
,"A380"
都可以匹配
匹配至少一个字符
正则表达式:
A\d+
其中 +
可以匹配至少一个字符,"A0"
,"A380"
都可以匹配,"A"
无法匹配,因为要求至少一个字符
匹配 0 个或一个字符
正则表达式:
A\d?
其中 ?
可以匹配 0 个或一个字符,"A"
,"A0"
都可以匹配,"A380"
无法匹配,因为超过 1 个字符就不能匹配了。
精确指定 n 个字符
A\d{3}
其中 {3}
可以匹配 3 个字符,"A380"
都可以匹配
匹配 n~m 个字符
A\d{3,5}
其中 {3,5}
可以匹配 3-5 个字符,"A380"
,"A3800"
,"A38000"
都可以匹配
A\d{3,}
其中 {3,}
可以匹配至少 3 个字符
练习
请编写一个正则表达式匹配国内的电话号码规则:3~4 位区号加 7~8 位电话,中间用 -
连接,例如:010-12345678
。
public static boolean isTelphone(String str) {
return str.matches("\\d{3,4}-\\d{7,8}");
}
3. 复杂匹配规则
匹配开头和结尾
用正则表达式进行多行匹配时,我们用 ^
表示开头,$
表示结尾。
正则表达式:
^A\d{3}$
"A001"
、"A380"
都可以匹配
匹配指定范围
正则表达式:
[abcd]1
其中 [abcd]
可以匹配 a, b, c, d 任意一个字符,"a1"
,"c1"
都可以匹配
上面的正则表达式等价于:
[a-d]1
如果要匹配多个范围可以这样写:
[a-dA-D]1
如果要匹配多个指定范围的字符,前面讲过的 {n}
仍然可以继续配合使用:
[a-dA-D]{6}
如果要匹配非指定范围的字符可以这样写:
[^a-dA-D]{6}
或规则匹配
正则表达式:
AB|CD
其中 |
连接的两个正则规则是或规则,AB
或 CD
都可以匹配
使用括号
如果我们想要匹配 learn AB 或 learn CD,这时就需要使用括号了,目的是把 AB|CD 作为一个整体。
learn\s(AB|CD)
4. 分组匹配
实际上()
还有一个重要作用,就是分组匹配。
例如匹配国内的电话号码规则:3~4 位区号加 7~8 位电话。正则表达式如下:
\d{3,4}-\d{7,8}
但是往往匹配成功后,下一步是提取区号和电话号码,于是问题来了:如何提取匹配的子串?
正确的方法是用 ()
先把要提取的规则分组,把上述正则表达式变为:
(\d{3,4})-(\d{7,8})
现在问题又来了:匹配后,如何按括号提取子串?
必须引入 java.util.regex
包,用 Pattern
对象匹配,匹配后获得一个 Matcher
对象,如果匹配成功,就可以直接从 Matcher.group(index)
返回子串:
public class test1 {
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 str1 = m.group(1);
String str2 = m.group(2);
System.out.println(str1);
System.out.println(str2);
} else {
System.out.println("匹配失败");
}
}
}
运行结果:
010
12345678
5. 非贪婪匹配
在介绍非贪婪匹配前,我们先看一个简单的问题:
给定一个字符串表示的数字,判断该数字末尾 0
的个数。例如:
"123000"
:3 个0
"10100"
:2 个0
"1001"
:0 个0
可以很容易地写出该正则表达式:(\d+)(0*)
,Java 代码如下:
public class test1 {
public static void main(String[] args) {
Pattern pattern = Pattern.compile("(\\d+)(0*)");
Matcher matcher = pattern.matcher("1230000");
if (matcher.matches()) {
System.out.println("group1=" + matcher.group(1));
System.out.println("group2=" + matcher.group(2));
}
}
}
运行结果:
group1=1230000
group2=
然而打印的第二个子串是空字符串 “”。
这是因为正则表达式默认使用贪婪匹配:任何一个规则,它总是尽可能多地向后匹配,因此,\d+
总是会把后面的 0 包含进来。
要让 \d+
尽量少匹配,让 0*
尽量多匹配,我们就必须让 \d+
使用非贪婪匹配。在规则 \d+
后面加个 ?
即可表示非贪婪匹配。
我们改写正则表达式如下:
public class test1 {
public static void main(String[] args) {
Pattern pattern = Pattern.compile("(\\d+?)(0*)");
Matcher matcher = pattern.matcher("1230000");
if (matcher.matches()) {
System.out.println("group1=" + matcher.group(1));
System.out.println("group2=" + matcher.group(2));
}
}
}
运行结果:
group1=123
group2=0000
6. 搜索和替换
分割字符串
使用正则表达式分割字符串可以实现更加灵活的功能。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" }
"a, b ;; c".split("[\\,\\;\\s]+"); // { "a", "b", "c" }
搜索字符串
我们获取到 Matcher
对象后,反复调用 find()
方法,在整个串中搜索能匹配上正则表达式的子串,并打印出来。
public class test1 {
public static void main(String[] args) {
String s = "The quick brown fox jumps over the lazy dog.";
// 查找 the,不区分大小写
Pattern p1 = Pattern.compile("the", Pattern.CASE_INSENSITIVE);
// 查找每一个单词
Pattern p2 = Pattern.compile("\\w+");
// 查找单词中包含 o 的单词
Pattern p3 = Pattern.compile("\\w*o\\w*");
Matcher m = p1.matcher(s);
while (m.find()) {
String sub = s.substring(m.start(), m.end());
System.out.println(sub + " start=" + m.start() + " end=" + m.end());
}
}
}
p1 正则表达式运行结果:
The start=0 end=3
the start=31 end=34
p2 正则表达式运行结果:
The start=0 end=3
quick start=4 end=9
brown start=10 end=15
fox start=16 end=19
jumps start=20 end=25
over start=26 end=30
the start=31 end=34
lazy start=35 end=39
dog start=40 end=43
p3 正则表达式运行结果:
brown start=10 end=15
fox start=16 end=19
over start=26 end=30
dog start=40 end=43
替换字符串
使用正则表达式替换字符串可以直接调用 String.replaceAll(),它的第一个参数是正则表达式,第二个参数是待替换的字符串。
public class test1 {
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.