正则表达式(Regular Expression,简称 Regex)是一种强大的文本处理工具,它使用一种特定的语法来描述和匹配字符串模式,在 Java 中,java.util.regex 包提供了对正则表达式的支持。

核心组件
Java 正则表达式主要由两个类组成:
java.util.regex.Pattern: 一个编译后的正则表达式模式,它是一个不可变的类,代表了一个经过编译的模式,你可以把它看作是“规则”本身。java.util.regex.Matcher: 对输入字符串进行解释和匹配的引擎,它是一个状态机器,用于执行Pattern定义的各种操作(如匹配、查找、替换等)。
使用正则表达式的基本流程是:
- 编译一个正则表达式字符串,得到一个
Pattern对象。 - 使用
Pattern对象和你的输入字符串创建一个Matcher对象。 - 通过
Matcher对象执行匹配操作并获取结果。
常用方法示例
下面我们通过一系列示例来学习最常用的方法。
matches() - 完全匹配
matches() 方法尝试将整个输入字符串与模式进行匹配,如果整个字符串完全匹配模式,则返回 true。

示例: 验证一个字符串是否是一个有效的手机号(假设规则是 11 位数字)。
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class RegexMatchesExample {
public static void main(String[] args) {
String regex = "^\\d{11}$"; // 正则表达式:^表示开始,$表示结束,\d表示数字,{11}表示11次
String phone1 = "13812345678";
String phone2 = "123456789012"; // 12位,不匹配
String phone3 = "138-1234-5678"; // 包含'-',不匹配
// 创建 Pattern 对象 (建议使用静态的 compile 方法)
Pattern pattern = Pattern.compile(regex);
// 测试 phone1
Matcher matcher1 = pattern.matcher(phone1);
System.out.println(phone1 + " 是否匹配: " + matcher1.matches()); // true
// 测试 phone2
Matcher matcher2 = pattern.matcher(phone2);
System.out.println(phone2 + " 是否匹配: " + matcher2.matches()); // false
// 测试 phone3
Matcher matcher3 = pattern.matcher(phone3);
System.out.println(phone3 + " 是否匹配: " + matcher3.matches()); // false
}
}
输出:
13812345678 是否匹配: true
123456789012 是否匹配: false
138-1234-5678 是否匹配: false
注意: ^ 和 在这里非常重要,如果没有它们,"abc12345678def" 也会被 ^\d{11}$ 匹配为 false,但如果只有 \d{11},它可能会找到子串 "12345678" 而导致 find() 返回 true。matches() 总是检查整个字符串。
find() 与 group() - 查找子串
find() 方法在输入字符串中查找与模式匹配的子串,如果找到,返回 true,Matcher 的状态会定位到该匹配项,你可以重复调用 find() 来查找所有匹配项。
找到匹配项后,可以使用 group() 方法获取匹配到的内容。
示例: 从一段文本中提取所有电子邮件地址。
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class RegexFindExample {
public static void main(String[] args) {
String text = "我的邮箱是 zhangsan@example.com,联系我,或者也可以联系 lisi@my-domain.co.uk。";
String regex = "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b";
// 在 Java 中,正则表达式默认不区分大小写,但最好明确指定
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(text);
System.out.println("找到的邮箱地址:");
while (matcher.find()) {
// group(0) 表示整个匹配的子串
System.out.println(matcher.group(0));
}
}
}
输出:
找到的邮箱地址:
zhangsan@example.com
lisi@my-domain.co.uk
解释:
\b: 单词边界,确保我们匹配的是完整的单词,而不是myaddress@example.com中的部分。[A-Za-z0-9._%+-]+: 匹配用户名部分,允许字母、数字、点、下划线、百分号、加号和减号。- 匹配 "@" 符号。
[A-Za-z0-9.-]+: 匹配域名部分。\.: 匹配点号( 在正则中有特殊含义,所以需要转义)。[A-Z|a-z]{2,}: 匹配顶级域名,如.com,.uk,.co等,至少2个字母。Pattern.CASE_INSENSITIVE: 编译模式时指定不区分大小写。
lookingAt() - 从开头匹配
lookingAt() 方法尝试从输入字符串的开头开始匹配模式,它不像 matches() 那样要求整个字符串都匹配,但必须是字符串的起始部分匹配即可。
示例:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class RegexLookingAtExample {
public static void main(String[] args) {
String text = "Hello world, this is a test.";
String regex = "Hello.*"; // 匹配以 "Hello" 开头,后面跟任意字符(除换行符)零次或多次
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
if (matcher.lookingAt()) {
System.out.println("字符串以 'Hello' 开头!");
System.out.println("匹配到的内容: " + matcher.group());
} else {
System.out.println("字符串不以 'Hello' 开头。");
}
}
}
输出:
字符串以 'Hello' 开头! Hello world, this is a test.
常用正则表达式元字符
掌握这些元字符是编写正则表达式的基础。
| 元字符 | 描述 | 示例 |
|---|---|---|
| 匹配除换行符以外的任意单个字符 | a.c 可以匹配 abc, aac, a!c |
|
^ |
匹配字符串的开始 | ^abc 匹配 abc123,但不匹配 123abc |
| 匹配字符串的结束 | abc$ 匹配 123abc,但不匹配 abc123 |
|
\d |
匹配一个数字字符,等同于 [0-9] |
\d{3} 匹配 123, 456 |
\D |
匹配一个非数字字符,等同于 [^0-9] |
\D+ 匹配 hello, |
\w |
匹配单词字符(字母、数字、下划线),等同于 [A-Za-z0-9_] |
\w+ 匹配 Java, user_123 |
\W |
匹配非单词字符,等同于 [^A-Za-z0-9_] |
\W 匹配 , , ` ` |
\s |
匹配任何空白字符(空格、制表符、换行符等) | \s+ 匹配 , "\t" |
\S |
匹配任何非空白字符 | \S+ 匹配 Java, 123 |
[...] |
字符类,匹配方括号内的任意一个字符 | [abc] 匹配 a, b, 或 c |
[^...] |
否定字符类,匹配不在方括号内的任意字符 | [^abc] 匹配 d, 1, |
| 匹配前面的元素零次或多次 | a* 匹配 (空串), a, aaa |
|
| 匹配前面的元素一次或多次 | a+ 匹配 a, aaa,但不匹配空串 |
|
| 匹配前面的元素零次或一次 | a? 匹配 或 a |
|
{n} |
精确匹配前面的元素 n 次 | \d{3} 匹配3个连续的数字 |
{n,} |
匹配前面的元素至少 n 次 | \d{2,} 匹配2个或更多连续的数字 |
{n,m} |
匹配前面的元素至少 n 次,最多 m 次 | \d{2,4} 匹配2到4个连续的数字 |
| 捕获组,将括号内的内容作为一个整体 | (ab)+ 匹配 ab, abab |
|
\| |
选择(或)操作符 | a\|b 匹配 a 或 b |
高级用法:分组和替换
分组与 group()
使用括号 可以创建捕获组。group(0) 总是整个匹配的字符串,group(1) 是第一个捕获组,group(2) 是第二个,以此类推。
示例: 提取日期字符串中的年、月、日。
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class RegexGroupExample {
public static void main(String[] args) {
String dateStr = "今天的日期是 2025-10-27。";
String regex = "(\d{4})-(\d{2})-(\d{2})";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(dateStr);
if (matcher.find()) {
System.out.println("完整日期: " + matcher.group(0));
System.out.println("年份: " + matcher.group(1));
System.out.println("月份: " + matcher.group(2));
System.out.println("日期: " + matcher.group(3));
}
}
}
输出:
完整日期: 2025-10-27
年份: 2025
月份: 10
日期: 27
替换操作 replaceAll() 和 replaceFirst()
Matcher 对象提供了强大的替换功能。
replaceAll(String replacement): 替换所有匹配的子串。replaceFirst(String replacement): 替换第一个匹配的子串。
在 replacement 字符串中,可以使用 $n 来引用第 n 个捕获组的内容。
示例: 将字符串中的所有日期格式从 YYYY-MM-DD 改为 DD/MM/YYYY。
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class RegexReplaceExample {
public static void main(String[] args) {
String text = "项目开始日期: 2025-01-15, 结束日期: 2025-12-31。";
String regex = "(\\d{4})-(\\d{2})-(\\d{2})";
// 替换模式:$3/$2/$1 表示用第三个捕获组,第二个,第一个
String replacement = "$3/$2/$1";
// 创建 Matcher 并进行替换
String result = Pattern.compile(regex).matcher(text).replaceAll(replacement);
System.out.println("原始文本: " + text);
System.out.println("替换后文本: " + result);
}
}
输出:
原始文本: 项目开始日期: 2025-01-15, 结束日期: 2025-12-31。
替换后文本: 项目开始日期: 15/01/2025, 结束日期: 31/12/2025。
Java 8+ 中的便捷方法
对于简单的匹配和查找,你不必总是手动创建 Pattern 和 Matcher 对象。String 类本身提供了一些便捷的静态方法,它们内部会为你处理这些对象。
String.matches(String regex): 等价于Pattern.matches(regex, this)。String.replaceAll(String regex, String replacement): 等价于Pattern.compile(regex).matcher(this).replaceAll(replacement)。String.replaceFirst(String regex, String replacement): 等价于Pattern.compile(regex).matcher(this).replaceFirst(replacement)。String.split(String regex): 根据正则表达式分割字符串。
示例:
public class StringRegexExample {
public static void main(String[] args) {
String str = "apple,banana;orange|grape";
// 使用 split 分割字符串
String[] fruits = str.split("[,;|]"); // 正则表达式:匹配逗号、分号或竖线
System.out.println("分割结果:");
for (String fruit : fruits) {
System.out.println(fruit);
}
// 使用 matches 验证
String email = "test@example.com";
boolean isValidEmail = email.matches("^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$");
System.out.println("\n邮箱 " + email + " 是否有效: " + isValidEmail);
}
}
输出:
分割结果:
apple
banana
orange
grape
邮箱 test@example.com 是否有效: true
| 操作 | 推荐方法 | 适用场景 |
|---|---|---|
| 完全匹配 | str.matches(regex) 或 pattern.matcher(str).matches() |
验证整个字符串是否符合某个格式(如邮箱、手机号)。 |
| 查找子串 | pattern.matcher(str).find() + matcher.group() |
从文本中提取符合特定模式的内容(如所有链接、所有数字)。 |
str.replaceAll(regex, replacement) 或 matcher.replaceAll(replacement) |
修改文本中符合模式的部分。 | |
| 分割字符串 | str.split(regex) |
根据复杂的分隔符(如多个符号)将字符串拆分为数组。 |
正则表达式非常强大,但也可能变得复杂,建议从简单的模式开始,逐步构建,并多使用在线测试工具(如 Regex101)来验证你的表达式。
