Java正则表达式
正则表达式的应用场景
最近(2025.8)在看《阿里巴巴 Java 开发手册》,其中有一条提到了正则表达式,这让我死去的记忆又复活了。
正则表达式(Regular Expressions)是一种强大的文本处理工具,广泛运用于对字符串、文本的操作场景,如搜索、编辑或者处理文本,它不是一种语言,而是一种文本模式,在每一种编程语言中的具体实现是有细微差别的,Java 中提供了 java.util.regex 包,它包含了 Pattern 和 Matcher 类,用于处理正则表达式的匹配操作。
正则表达式的语法
基础匹配符号
英文句号
.:匹配单个任何字符(除换行符外)示例:
t.n可以匹配ten、tan、t1n、t*n等,但不能匹配taan、tn、t然n
方括号
[]:只有方括号内指定的字符才参与匹配,匹配单个字符示例:
t[abcd]n可以匹配tan、tbn、tcn、tdn,但不能匹配thn、tabn、tn
或符号
|:相当于“或”,选择其中一项进行匹配示例:
t(a|b|c|dd)n可以匹配tan、tbn、tcn、tddn,但不能匹配taan、tn、tabcn
范围表示
-:在方括号内表示字符范围示例:
[A-Z]匹配任意大写字母,[a-z]匹配任意小写字母,[0-9]匹配任意数字注意:连字符
-只有在字符类内部且不在首位时才表示范围,否则就是字面量
排除符号
^:在方括号内首位使用时表示排除示例:
[^abc]匹配除了 a、b、c 以外的任意字符注意:方括号外的
^表示字符串开始位置
表示匹配次数的符号
字符类快捷符号
位置锚点符号
特殊转义字符
由于其本身有特殊含义,需要前面加 \ 来表达其真实含义:
\.匹配句点\\匹配反斜杠\^匹配脱字符\$匹配美元符号\?匹配问号\+匹配加号\*匹配星号\(匹配左括号\)匹配右括号\[匹配左方括号\]匹配右方括号\{匹配左花括号\}匹配右花括号\|匹配竖线
常用逻辑符号
先行断言示例:
// 匹配后面跟着"们"的汉字
Pattern.compile("\w(?=们)");
// 匹配后面不是数字的字母
Pattern.compile("[a-z]+(?!\d)");贪婪与非贪婪匹配
默认情况下,*、+、?、{n,m} 是贪婪的,会匹配尽可能多的字符。在其后添加 ? 可变为非贪婪(懒惰)模式。
示例:
String str = "<div>content</div><span>text</span>";
// 贪婪匹配:匹配整个字符串
Pattern greedy = Pattern.compile("<.*>");
Matcher m1 = greedy.matcher(str);
m1.find(); // 匹配 "<div>content</div><span>text</span>"
// 非贪婪匹配:只匹配第一个标签
Pattern lazy = Pattern.compile("<.*?>");
Matcher m2 = lazy.matcher(str);
m2.find(); // 匹配 "<div>"常用的正则表达式
一、校验数字的表达式
二、校验字符的表达式
三、特殊需求表达式
java.util.regex 包
java.util.regex 包是 Java 标准库中用于支持正则表达式操作的包,主要包含以下三个类:
Pattern 类:正则表达式的编译表示。没有公共构造方法,通过静态方法
compile()创建实例Matcher 类:对输入字符串进行解释和匹配操作的引擎。通过 Pattern 对象的
matcher()方法获得PatternSyntaxException:非强制异常类,表示正则表达式模式中的语法错误
Pattern 类常用方法
// 编译正则表达式
Pattern pattern = Pattern.compile("\\w+");
// 返回正则表达式的字符串形式
String regex = pattern.pattern(); // 返回 "\w+"
// 分隔字符串,返回 String[]
Pattern p = Pattern.compile("\\d+");
String[] str = p.split("我的姓名是:456456我的电话是:0532214");
// 结果:str[0]="我的姓名是:" str[1]="我的电话是:"
// 快捷匹配(一次性使用)
boolean isMatch = Pattern.matches("\\d+", "12345"); // 返回 true
// 忽略大小写
Pattern p2 = Pattern.compile("java", Pattern.CASE_INSENSITIVE);
// 多行模式(^和$匹配每行的开头和结尾)
Pattern p3 = Pattern.compile("^\\d+", Pattern.MULTILINE);
// 点号匹配所有字符(包括换行符)
Pattern p4 = Pattern.compile(".*", Pattern.DOTALL);Pattern 常用标志常量
Matcher 类常用方法
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher("There are 123 apples and 456 oranges.");
// find() - 查找下一个匹配子串
while (matcher.find()) {
System.out.println(matcher.group()); // 输出 123 和 456
}
// matches() - 整个字符串是否完全匹配
boolean fullMatch = matcher.matches();
// lookingAt() - 字符串开头是否匹配
boolean startMatch = matcher.lookingAt();
// group() - 获取匹配的子字符串
System.out.println(matcher.group()); // 整个匹配
System.out.println(matcher.group(1)); // 第一个捕获组
// start() / end() - 获取匹配的起始和结束索引
int start = matcher.start();
int end = matcher.end();
// reset() - 重置匹配器
matcher.reset(); // 重置到字符串开头
matcher.reset("new input string");
// replaceAll() / replaceFirst() - 替换
String result1 = matcher.replaceAll("#"); // 替换所有
String result2 = matcher.replaceFirst("#"); // 替换第一个
// appendReplacement() / appendTail() - 高级替换
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(sb, "数字");
}
matcher.appendTail(sb);捕获组的使用
捕获组使用圆括号 () 定义,按左括号出现的顺序从 1 开始编号,group(0) 表示整个匹配。
// 基本捕获组
Pattern pattern = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})");
Matcher matcher = pattern.matcher("2025-08-15");
if (matcher.matches()) {
System.out.println("Year: " + matcher.group(1)); // 2025
System.out.println("Month: " + matcher.group(2)); // 08
System.out.println("Day: " + matcher.group(3)); // 15
}
// 命名捕获组(Java 7+)
Pattern namedPattern = Pattern.compile("(?<year>\\d{4})-(?<month>\\d{2})-(?<day>\\d{2})");
Matcher namedMatcher = namedPattern.matcher("2025-08-15");
if (namedMatcher.matches()) {
System.out.println("Year: " + namedMatcher.group("year"));
}
// 非捕获组 (?:...) - 不保存匹配结果
Pattern nonCapture = Pattern.compile("a(?:bc)*d");
Matcher ncm = nonCapture.matcher("abcbcbcd");
System.out.println(ncm.matches()); // true
// 捕获组的数量
int groupCount = matcher.groupCount();完整示例代码
import java.util.regex.*;
public class RegexExample {
public static void main(String[] args) {
// 示例1:基本匹配
String content = "I am alan";
String pattern = ".*alan.*";
boolean isMatch = Pattern.matches(pattern, content);
System.out.println("是否包含 'alan'? " + isMatch);
// 示例2:提取数字
Pattern numPattern = Pattern.compile("\\d+");
Matcher numMatcher = numPattern.matcher("订单号: 12345, 金额: 678.90");
while (numMatcher.find()) {
System.out.println("找到数字: " + numMatcher.group());
}
// 示例3:验证邮箱
String email = "user@example.com";
String emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$";
boolean isValidEmail = Pattern.matches(emailRegex, email);
System.out.println("邮箱有效: " + isValidEmail);
// 示例4:替换操作
Pattern replacePattern = Pattern.compile("\\d+");
Matcher replaceMatcher = replacePattern.matcher("有 100 个苹果和 200 个橘子");
String result = replaceMatcher.replaceAll("#");
System.out.println(result); // 输出 "有 # 个苹果和 # 个橘子"
// 示例5:分割字符串
Pattern splitPattern = Pattern.compile("[,;\\s]+");
String[] items = splitPattern.split("apple, banana;orange grape");
for (String item : items) {
System.out.println(item);
}
// 示例6:使用正则表达式验证手机号
String phone = "13812345678";
String phoneRegex = "^(13[0-9]|14[5|7]|15[0-9]|18[0-9])\\d{8}$";
System.out.println("手机号有效: " + phone.matches(phoneRegex));
}
}常见的 PatternSyntaxException 原因
性能优化建议
预编译正则表达式:重复使用的正则表达式应编译为 Pattern 对象
// 不推荐:每次调用都重新编译 public boolean isValid(String input) { return Pattern.matches("^\\d+$", input); } // 推荐:预编译为 static final 常量 private static final Pattern NUMBER_PATTERN = Pattern.compile("^\\d+$"); public boolean isValid(String input) { return NUMBER_PATTERN.matcher(input).matches(); }避免灾难性回溯:避免使用嵌套的量词,如
(a+)*或(a*)*使用 possessive 量词(Java 支持):
++、*+、?+、{n,m}+,防止回溯// 贪婪量词可能导致大量回溯 Pattern bad = Pattern.compile("(a|b)*b"); // possessive 量词不回溯,性能更好 Pattern good = Pattern.compile("(a|b)*+b");明确使用边界:使用
^和$减少匹配范围