正则表达式, regular expression,可以用来搜索和处理文本。
语法
正则表达式中有一些字符代表着特定的含义,比如+
表示匹配前面的字符/表达式0到多次。这种特定含义的字符,可以看做是正则表达式语言中的保留关键词
,有些资料中也称作为元字符
。有些元字符之间需要成对出现,例如 {
和 }
。元字符之前也可以搭配使用。有些元字符,在不同的使用场景中含义不同。
语法 | 说明 |
---|---|
^ | 匹配开始位置 |
$ | 匹配结束位置 |
\ | 转义 |
* | 匹配前面的字符/表达式 0 次或多次 |
+ | 匹配前面的字符/表达式 1 次或多次 |
? | 匹配前面的字符/表达式 0 次或1次。在 * 、+ 、? 、{m} 、{m,} 、{m,n} 之后出现时,代表匹配模式是非贪心的 。 |
. | 默认模式下, 匹配\r 、\n 外的所有单个字符。 |
{m} | 匹配前面的字符/表达式 m 次。 m >=0 。默认是贪心匹配 。 |
{m, n} | 匹配前面的字符/表达式 m 次到 n 次。 m >=0, n >= m。默认是贪心匹配 。 |
{m,} | 匹配前面的字符/表达式至少 m 次。 m >=0。默认是贪心匹配 。 |
a|b | 匹配 a 或者 b |
a|xyz | 匹配 a 或者 xyz |
[xyz] | 匹配 x 或者 y 或者 z |
[^xyz] | 匹配不是 x、y、z 的字符 |
[a-z] | 匹配 a 到 z 之间的字符,包含 a、z。 |
[^a-z] | 匹配不是 a 到 z 之间的字符,a、z 也不匹配。 |
[0-9] | 匹配 0 到 9 之间的字符,包含 0、9。 |
(pattern) | 匹配 pattern 对应的正则表达式,并生成捕获组 |
(?:pattern) | 匹配 pattern 对应的正则表达式,不生成捕获组 |
(?<groupName>pattern) |
匹配 pattern 对应的正则表达式,并生成捕获组 ,组名是groupName 。 |
\1 | 反向引用前面的编号为1的捕获组。不会生成捕获组。类似的,还有\2 、\3 等。 |
\b | 匹配单词边界。在英文中,单词之间用空格隔开。单词边界是指单词和空格之间的部分。这是一个抽象的概念,因为单词和空格之间的部分是看不到的。 |
\B | 匹配非单词边界。 |
\d | 匹配数字字符。即[0-9] |
\D | 匹配非数字字符。即[^0-9] |
\n | 匹配换行符 |
\r | 匹配回车符 |
\f | 匹配换页符 |
\t | 匹配制表符 |
\v | 匹配垂直制表符 |
\s | 配置空白符号。即[\n\r\f\t\v] |
\S | 匹配非空白符号 |
\w | 匹配字母、数字、下划线。即[A-Za-z0-9_] |
\W | 不匹配字母、数字、下划线。即[^A-Za-z0-9_] |
\x23 | 匹配 ascii 表中十六进制的 23 对应的字符。\x 后必须是2个16进制字符。 |
\u1234 | 匹配 16 进制的 1234 对应的 unicode 字符。\u 后必须是4个16进制字符。这是 UTF-16 的字符编码。表情符号以及一些生僻字,会需要两个\u才行。例如\ud83d\ude0a 对应 😊 。 |
其他字符 | 匹配对应的字符 |
贪心匹配
是尽可能多的匹配,非贪心匹配
是尽可能少的匹配。
转义规则
在正则表达式中?
代表匹配前面的字符/表达式0次或者1次。如果要匹配字符?
,那么正则表达式处理引擎应该看到的是\?
。我们在 Java 中书写的字符串形式的正则表达式应该是 \\?
。
要匹配字符\
,Java 中要写成\\\\
。
注意,在其他一些编程语言中,匹配字符?
,只需要\?
即可, \\?
反而是错误的。
使用 String 的 matches 方法判断字符串是否匹配正则表达式
matches 返回 boolen 值。
示例: 判断字符串中是否有英文句号.
public class RegexTest {
public static void main(String[] args) {
String regex = "(\\s|\\S)*\\.(\\s|\\S)*";
System.out.println( "".matches(regex) ); // false
System.out.println( "你好".matches(regex) ); // false
System.out.println( "你好.".matches(regex) ); // true
System.out.println( "你好 .".matches(regex) ); // true
System.out.println( "你好 . 世界".matches(regex) ); // true
System.out.println( "你好\n .".matches(regex) ); // true
}
}
\s
用于匹配空白字符,\S
匹配非空白字符。\.
用于匹配英文逗号.
示例: 判断字符串不为空,且只含有数字
public class RegexTest {
public static void main(String[] args) {
String regex = "[0-9]+";
System.out.println( "".matches(regex) ); // false
System.out.println( "你好".matches(regex) ); // false
System.out.println( "0".matches(regex) ); // true
System.out.println( "012".matches(regex) ); // true
System.out.println( "129".matches(regex) ); // true
System.out.println( "129.".matches(regex) ); // false
}
}
示例: 判断字符串是整数或小数, 小数点后最多8位
public class RegexTest {
public static void main(String[] args) {
String regex = "[0-9]+\\.?[0-9]{0,8}";
System.out.println( "".matches(regex) ); // false
System.out.println( "你好".matches(regex) ); // false
System.out.println( "0".matches(regex) ); // true
System.out.println( "012".matches(regex) ); // true
System.out.println( "129".matches(regex) ); // true
System.out.println( "129.".matches(regex) ); // true
System.out.println( "0129.".matches(regex) ); // true
System.out.println( "129.000".matches(regex) ); // true
System.out.println( "129.000111".matches(regex) ); // true
System.out.println( "129.000111222".matches(regex) ); // false
System.out.println( "129x00222".matches(regex) ); // false
}
}
012
也返回 true,在一些场景下,是不符合预期的。
示例: 判断字符串含有小数点,且小数点前后都是数字,且小数点前若有多个数字,最高位不能是0
public class RegexTest {
public static void main(String[] args) {
String regex = "(([1-9]+[0-9]*)|0)\\.[0-9]+";
System.out.println( "".matches(regex) ); // false
System.out.println( "你好".matches(regex) ); // false
System.out.println( "0".matches(regex) ); // false
System.out.println( "012".matches(regex) ); // false
System.out.println( "129".matches(regex) ); // false
System.out.println( "129.".matches(regex) ); // false
System.out.println( "0129.".matches(regex) ); // false
System.out.println( "129.000".matches(regex) ); // true
System.out.println( "129.000111".matches(regex) ); // true
System.out.println( "129.000111222".matches(regex) ); // true
System.out.println( "129x00222".matches(regex) ); // false
}
}
示例: 判断字符串为空,或者仅由字母abc中的一个或多个组成
public class RegexTest {
public static void main(String[] args) {
String regex = "[abc]*";
System.out.println( "".matches(regex) ); // true
System.out.println( "a".matches(regex) ); // true
System.out.println( "ab".matches(regex) ); // true
System.out.println( "abc".matches(regex) ); // true
System.out.println( "abccba".matches(regex) ); // true
System.out.println( "abc-cba".matches(regex) ); // false
System.out.println( "ABC".matches(regex) ); // false
}
}
使用 Pattern.matches 方法判断字符串是否匹配正则表达式
String 的 matches 方法,底层调用的 Pattern.matches 方法。
Pattern.matches 方法源码:
public static boolean matches(String regex, CharSequence input) {
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(input);
return m.matches();
}
使用示例:
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String regex = "[abc]*";
System.out.println( Pattern.matches(regex, "a") ); // true
System.out.println( Pattern.matches(regex, "123") ); // false
}
}
使用 Pattern.compile 编译正则表达式
示例:
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String regex = "[abc]*";
Pattern pattern = Pattern.compile(regex);
System.out.println( pattern.matcher("a").matches() ); // true
System.out.println( pattern.matcher("123").matches() ); // false
}
}
使用 Matcher 找到匹配正则表达式的字符串
示例: 使用 find(), start(), end()
find() 返回是否找到匹配正则表达式的字符串。start()、end() 返回所匹配的字符串的位置。
第一次执行 find() 时,会从字符串初始位置开始查找。在找到了匹配的字符串后,会变更下次查找的初始位置。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String regex = "[abc]+";
String data = "a123bc";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(data);
while (matcher.find()) {
System.out.printf(
"start: %d, end: %s, string: %s\n",
matcher.start(),
matcher.end(),
data.substring(matcher.start(), matcher.end())
);
}
}
}
执行结果:
start: 0, end: 1, string: a
start: 4, end: 6, string: bc
理解【捕获组】
在正则表达式中,可以应该能括号对匹配的内容进行分组。我们可以利用这个机制来提取我们需要的内容。
例如,正则表达式 ((a)(b(c)))
,会匹配 abc
这样的连续字符。组编号和内容如下:
组编号 | 内容 |
---|---|
1 | ((a)(b(c))) |
2 | (a) |
3 | (b(c)) |
4 | (c) |
示例: 获取捕获组的数量和每个组的内容
代码示例1:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String regex = "((a)(b(c)))";
String data = "abc-ab";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(data);
while (matcher.find()) {
System.out.println("groupCount: " + matcher.groupCount()); // 获取分组数量
System.out.println("group(0): " + matcher.group(0)); // 0 不代表捕获组, 代表匹配的字符串
System.out.println("group(1): " + matcher.group(1));
System.out.println("group(2): " + matcher.group(2));
System.out.println("group(3): " + matcher.group(3));
System.out.println("group(4): " + matcher.group(4));
}
}
}
执行结果:
groupCount: 4
group(0): abc
group(1): abc
group(2): a
group(3): bc
group(4): c
注意, group(0)
是匹配的字符串,不算在捕获组中。
代码示例2:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String regex = "((a)(b(c)))[a-z]*";
String data = "abc-abcd";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(data);
while (matcher.find()) {
System.out.println("-------");
System.out.println("groupCount: " + matcher.groupCount());
System.out.printf("start: %d, end: %s\n", matcher.start(), matcher.end());
System.out.println("group(0): " + matcher.group(0));
System.out.println("group(1): " + matcher.group(1));
System.out.println("group(2): " + matcher.group(2));
System.out.println("group(3): " + matcher.group(3));
System.out.println("group(4): " + matcher.group(4));
}
}
}
执行结果:
groupCount: 4
start: 0, end: 3
group(0): abc
group(1): abc
group(2): a
group(3): bc
group(4): c
-------
groupCount: 4
start: 4, end: 8
group(0): abcd
group(1): abc
group(2): a
group(3): bc
group(4): c
代码示例3:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String regex = "<h2>(.*)</h2>";
String data = "你好<h2>世界</h2>";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(data);
while (matcher.find()) {
System.out.println(matcher.group(1));
}
}
}
执行结果:
世界
代码示例4:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String regex = "[abc]+";
String data = "a123bc";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(data);
System.out.println( matcher.groupCount() );
}
}
执行结果:
0
示例: 命名捕获组
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String regex = "(?<num>[0-9]+)";
String data = "123-789";
Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
Matcher matcher = pattern.matcher(data);
while (matcher.find()) {
System.out.println( matcher.group() );
System.out.println( matcher.group("num") );
System.out.println("----");
}
}
}
运行结果:
123
123
----
789
789
----
示例: 反向引用
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String regex = "([a-z]*)-\\1";
String data = "abc-abcd@mail.com";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(data);
while (matcher.find()) {
System.out.println("-------");
System.out.println("groupCount: " + matcher.groupCount());
System.out.println("group(0): " + matcher.group(0));
System.out.println("group(1): " + matcher.group(1));
}
}
}
执行结果:
-------
groupCount: 1
group(0): abc-abc
group(1): abc
String 的 replaceFirst、replaceAll 的特殊用法:调整捕获组位置
replaceFirst、replaceAll 的第一个参数是正则表达式,第二个参数是要替换的内容。但是第2个参数比较特殊, 在这里面$1
代表第1个捕获组的内容,$2
代表第2个捕获组的内容,依次类推。
如果要替换为字符串$1
,要写成\\$1
。
代码示例:
public class RegexTest {
public static void main(String[] args) {
String regex = "([abc]+)-([a-z]+)";
System.out.println( "ab-ccc".replaceFirst(regex, "$2-$1"));
System.out.println( "ab-ccc".replaceFirst(regex, "\\$2-$1"));
}
}
执行结果:
ccc-ab
$2-ab
贪心匹配与非贪心匹配
贪心匹配
是尽可能多的匹配,非贪心匹配
是尽可能少的匹配。
*
、+
、?
、{m}
、{m,}
、{m,n}
默认是贪心匹配
。当?
出现在他们后面时,代表非贪心匹配
。
示例:贪心匹配
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String regex = "[abc]+";
String data = "a123bc";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(data);
while (matcher.find()) {
System.out.printf(
"start: %d, end: %s, string: %s\n",
matcher.start(),
matcher.end(),
data.substring(matcher.start(), matcher.end())
);
}
}
}
执行结果:
start: 0, end: 1, string: a
start: 4, end: 6, string: bc
示例: 非贪心匹配
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String regex = "[abc]+?";
String data = "a123bc";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(data);
while (matcher.find()) {
System.out.printf(
"start: %d, end: %s, string: %s\n",
matcher.start(),
matcher.end(),
data.substring(matcher.start(), matcher.end())
);
}
}
}
执行结果:
start: 0, end: 1, string: a
start: 4, end: 5, string: b
start: 5, end: 6, string: c
使用 String 的 replaceAll 替换掉符合正则表达式的字符串
replaceAll 会替换掉所有符合正则表达式的子字符串。
代码示例:
public class RegexTest {
public static void main(String[] args) {
String regex = "[abc]+";
System.out.println( "ab123abc".replaceAll(regex, "替换后"));
}
}
执行结果:
替换后123替换后
使用 String 的 replaceFirst 替换掉符合正则表达式的字符串
replaceFirst 会替换掉第一个符合正则表达式的子字符串。
代码示例:
public class RegexTest {
public static void main(String[] args) {
String regex = "[abc]+";
System.out.println( "ab123abc".replaceFirst(regex, "替换后"));
}
}
执行结果:
替换后123abc
使用 Pattern.DOTALL 让英文句号.
支持匹配\r
、\n
默认是不匹配换行符的。
代码示例:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String regex = ".*";
String data = "aaaa\nbbbbb";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(data);
if (matcher.find()) {
System.out.println( matcher.group() );
}
}
}
执行结果:
aaaa
Pattern.compile 设置模式为 Pattern.DOTALL 时,则支持匹配\r
、\n
。
代码示例:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String regex = ".*";
String data = "aaaa\nbbbbb";
Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
Matcher matcher = pattern.matcher(data);
if (matcher.find()) {
System.out.println( matcher.group() );
}
}
}
执行结果:
aaaa
bbbbb
使用 ^
、$
匹配开始位置和结束位置
示例: 使用 ^
匹配开始位置
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String regex = "^[0-9]+";
String data = "123abc456";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(data);
while (matcher.find()) {
System.out.println( matcher.group() );
}
}
}
执行结果:
123
只匹配到了 123, 没有匹配到 456。
###示例: 使用 $
匹配结束位置
代码示例1
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String regex = "[0-9]+$";
String data = "123abc456";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(data);
while (matcher.find()) {
System.out.println( matcher.group() );
}
}
}
执行结果:
456
代码示例2
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String regex = "[0-9]+$";
String data = "123abc456\n789";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(data);
while (matcher.find()) {
System.out.println( matcher.group() );
}
}
}
执行结果:
789
为什么没有匹配到 456 ?因为默认情况下结束位置是指整个文本的结束位置,而不是每一行的结束位置。
若要匹配到456
,可以指定Pattern.MULTILINE
来实现。见下面的示例。
示例: 使用Pattern.MULTILINE
让 $
匹配每一行的结束位置
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
String regex = "[0-9]+$";
String data = "123abc456\n789";
Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
Matcher matcher = pattern.matcher(data);
while (matcher.find()) {
System.out.println( matcher.group() );
}
}
}
执行结果:
456
789