共计 3259 个字符,预计需要花费 9 分钟才能阅读完成。
正则表达式的匹配规则是从左到右按规则匹配。我们首先来看如何使用正则表达式来做精确匹配。
对于正则表达式 abc
来说,它只能精确地匹配字符串 "abc"
,不能匹配"ab"
,"Abc"
,"abcd"
等其他任何字符串。
如果正则表达式有特殊字符,那就需要用 \
转义。例如,正则表达式 a\&c
,其中\&
是用来匹配特殊字符 &
的,它能精确匹配字符串 "a&c"
,但不能匹配"ac"
、"a-c"
、"a&&c"
等。
要注意正则表达式在 Java 代码中也是一个字符串,所以,对于正则表达式 a\&c
来说,对应的 Java 字符串是 "a\\&c"
,因为\
也是 Java 字符串的转义字符,两个 \\
实际上表示的是一个\
:
// regex
public class Main {public static void main(String[] args) {String re1 = "abc";
System.out.println("abc".matches(re1));
System.out.println("Abc".matches(re1));
System.out.println("abcd".matches(re1));
String re2 = "a\\&c"; // 对应的正则是 a\&c
System.out.println("a&c".matches(re2));
System.out.println("a-c".matches(re2));
System.out.println("a&&c".matches(re2));
}
}
如果想匹配非 ASCII 字符,例如中文,那就用 \u####
的十六进制表示,例如:a\u548cc
匹配字符串 "a 和 c"
,中文字符 和
的 Unicode 编码是548c
。
匹配任意字符
精确匹配实际上用处不大,因为我们直接用 String.equals()
就可以做到。大多数情况下,我们想要的匹配规则更多的是模糊匹配。我们可以用 .
匹配一个任意字符。
例如,正则表达式 a.c
中间的 .
可以匹配一个任意字符,例如,下面的字符串都可以被匹配:
"abc"
,因为.
可以匹配字符b
;"a&c"
,因为.
可以匹配字符&
;"acc"
,因为.
可以匹配字符c
。
但它不能匹配 "ac"
、"a&&c"
,因为.
匹配一个字符且仅限一个字符。
匹配数字
用 .
可以匹配任意字符,这个口子开得有点大。如果我们只想匹配 0
~9
这样的数字,可以用 \d
匹配。例如,正则表达式 00\d
可以匹配:
"007"
,因为\d
可以匹配字符7
;"008"
,因为\d
可以匹配字符8
。
它不能匹配 "00A"
,"0077"
,因为\d
仅限单个数字字符。
匹配常用字符
用 \w
可以匹配一个字母、数字或下划线,w 的意思是 word。例如,java\w
可以匹配:
"javac"
,因为\w
可以匹配英文字符c
;"java9"
,因为\w
可以匹配数字字符9
;。"java_"
,因为\w
可以匹配下划线_
。
它不能匹配 "java#"
,"java"
,因为\w
不能匹配#
、空格等字符。
匹配空格字符
用 \s
可以匹配一个空格字符,注意空格字符不但包括空格
,还包括 tab 字符(在 Java 中用\t
表示)。例如,a\sc
可以匹配:
"a c"
,因为\s
可以匹配空格字符"a c"
,因为\s
可以匹配 tab 字符\t
。
它不能匹配 "ac"
,"abc"
等。
匹配非数字
用 \d
可以匹配一个数字,而 \D
则匹配一个非数字。例如,00\D
可以匹配:
"00A"
,因为\D
可以匹配非数字字符A
;"00#"
,因为\D
可以匹配非数字字符#
。
00\d
可以匹配的字符串 "007"
,"008"
等,00\D
是不能匹配的。
类似的,\W
可以匹配 \w
不能匹配的字符,\S
可以匹配 \s
不能匹配的字符,这几个正好是反着来的。
// regex
public class Main {public static void main(String[] args) {String re1 = "java\\d"; // 对应的正则是 java\d
System.out.println("java9".matches(re1));
System.out.println("java10".matches(re1));
System.out.println("javac".matches(re1));
String re2 = "java\\D";
System.out.println("javax".matches(re2));
System.out.println("java#".matches(re2));
System.out.println("java5".matches(re2));
}
}
重复匹配
我们用 \d
可以匹配一个数字,例如,A\d
可以匹配"A0"
,"A1"
,如果要匹配多个数字,比如"A380"
,怎么办?
修饰符 *
可以匹配任意个字符,包括 0 个字符。我们用 A\d*
可以匹配:
A
:因为\d*
可以匹配 0 个数字;A0
:因为\d*
可以匹配 1 个数字0
;A380
:因为\d*
可以匹配多个数字380
。
修饰符 +
可以匹配至少一个字符。我们用 A\d+
可以匹配:
A0
:因为\d+
可以匹配 1 个数字0
;A380
:因为\d+
可以匹配多个数字380
。
但它无法匹配 "A"
,因为修饰符+
要求至少一个字符。
修饰符 ?
可以匹配 0 个或一个字符。我们用 A\d?
可以匹配:
A
:因为\d?
可以匹配 0 个数字;A0
:因为\d?
可以匹配 1 个数字0
。
但它无法匹配 "A380"
,因为修饰符?
超过 1 个字符就不能匹配了。
如果我们想精确指定 n 个字符怎么办?用修饰符 {n}
就可以。A\d{3}
可以精确匹配:
A380
:因为\d{3}
可以匹配 3 个数字380
。
如果我们想指定匹配 n~m 个字符怎么办?用修饰符 {n,m}
就可以。A\d{3,5}
可以精确匹配:
A380
:因为\d{3,5}
可以匹配 3 个数字380
;A3800
:因为\d{3,5}
可以匹配 4 个数字3800
;A38000
:因为\d{3,5}
可以匹配 5 个数字38000
。
如果没有上限,那么修饰符 {n,}
就可以匹配至少 n 个字符。
练习
请编写一个正则表达式匹配国内的电话号码规则:3~4 位区号加 7~8 位电话,中间用 -
连接,例如:010-12345678
。
// regex
import java.util.*;
public class Main {public static void main(String[] args) throws Exception {String re = "\\d";
for (String s : List.of("010-12345678", "020-9999999", "0755-7654321")) {if (!s.matches(re)) {System.out.println("测试失败:" + s);
return;
}
}
for (String s : List.of("010 12345678", "A20-9999999", "0755-7654.321")) {if (s.matches(re)) {System.out.println("测试失败:" + s);
return;
}
}
System.out.println("测试成功!");
}
}
下载练习
进阶:国内区号必须以 0 开头,而电话号码不能以 0 开头,试修改正则表达式,使之能更精确地匹配。
提示:\d
和 \D
这种简单的规则暂时做不到,我们需要更复杂规则,后面会详细讲解。
小结
单个字符的匹配规则如下:
正则表达式 | 规则 | 可以匹配 |
---|---|---|
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 |