Java 正则表达式详解
正则表达式(Regular Expression)是一种强大的文本处理工具,它通过定义特定模式来匹配、查找和替换文本。Java 自 JDK 1.4 起内置了对正则表达式的支持,主要通过
java.util.regex 包中的 Pattern、Matcher 和 PatternSyntaxException 类实现。本文将系统讲解 Java 正则表达式的核心概念、语法规则及实战技巧。一、Java 正则表达式基础
1. 核心类与工作流程
Java 正则表达式的处理主要涉及两个核心类:
Pattern类:编译正则表达式后创建的模式对象,线程安全,可复用Matcher类:对输入字符串进行匹配操作的引擎,线程不安全
基本工作流程:
import java.util.regex.*;
public class RegexExample {
public static void main(String[] args) {
// 1. 编译正则表达式,生成Pattern对象
Pattern pattern = Pattern.compile("a*b");
// 2. 创建Matcher对象,关联待匹配的字符串
Matcher matcher = pattern.matcher("aaaaab");
// 3. 执行匹配操作
boolean matchFound = matcher.matches();
System.out.println(matchFound); // 输出: true
}
}
2. 正则表达式语法基础
Java 正则表达式支持的基本元字符和语法包括:
(1)字符类
[abc]:匹配 a、b 或 c 中的任意一个字符[^abc]:匹配除 a、b、c 之外的任意字符[a-zA-Z]:匹配任意大小写字母[0-9]:匹配任意数字
(2)预定义字符类
.:匹配任意单个字符(除换行符)\d:等价于[0-9],匹配数字\D:等价于[^0-9],匹配非数字\s:匹配空白字符(空格、制表符、换行符等)\S:匹配非空白字符\w:等价于[a-zA-Z0-9_],匹配字母、数字和下划线\W:等价于[^a-zA-Z0-9_],匹配非字母、数字和下划线
(3)数量词
X?:X 出现 0 次或 1 次(可选)X*:X 出现 0 次或多次X+:X 出现 1 次或多次X{n}:X 恰好出现 n 次X{n,}:X 至少出现 n 次X{n,m}:X 出现次数介于 n 和 m 之间(含)
(4)边界匹配
^:匹配输入的开始位置$:匹配输入的结束位置\b:匹配单词边界(如空格、标点或字符串首尾)
二、Matcher 类的核心方法
Matcher 类提供了三个主要方法用于执行匹配操作:1. matches()
- 功能:尝试将整个输入字符串与正则表达式匹配
- 返回值:完全匹配返回
true,否则返回false
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher("12345");
System.out.println(matcher.matches()); // true
matcher = pattern.matcher("123abc");
System.out.println(matcher.matches()); // false
2. find()
- 功能:在输入字符串中查找下一个匹配的子序列
- 返回值:找到匹配返回
true,否则返回false - 特点:可多次调用,用于查找所有匹配项
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher("abc123def456");
while (matcher.find()) {
System.out.println("匹配到: " + matcher.group());
}
// 输出:
// 匹配到: 123
// 匹配到: 456
3. lookingAt()
- 功能:从输入字符串的开始位置尝试匹配正则表达式
- 返回值:如果字符串的前缀匹配模式,返回
true,否则false - 与
matches()的区别:不要求整个字符串匹配,只检查前缀
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher("123abc");
System.out.println(matcher.lookingAt()); // true(前缀"123"匹配)
System.out.println(matcher.matches()); // false(整个字符串不匹配)
三、分组与捕获
1. 基本分组
- 语法:使用圆括号
()定义分组 - 编号规则:从 1 开始,按左括号出现的顺序编号
- 特殊组:组 0 表示整个匹配的文本
Pattern pattern = Pattern.compile("(\\d{3})-(\\d{4})");
Matcher matcher = pattern.matcher("电话: 010-1234");
if (matcher.find()) {
System.out.println("完整匹配: " + matcher.group(0)); // 010-1234
System.out.println("第一组: " + matcher.group(1)); // 010
System.out.println("第二组: " + matcher.group(2)); // 1234
}
2. 非捕获分组
- 语法:
(?:pattern) - 作用:仅用于分组,但不捕获匹配的文本,不分配组号
- 优势:提高性能,减少内存占用
// 非捕获分组示例:匹配IP地址
Pattern pattern = Pattern.compile("(?:\\d{1,3}\\.){3}\\d{1,3}");
Matcher matcher = pattern.matcher("IP: 192.168.1.1");
if (matcher.find()) {
System.out.println("匹配到IP: " + matcher.group()); // 192.168.1.1
}
3. 反向引用
- 语法:
\n(n 为组号) - 作用:在正则表达式内部引用前面捕获的分组内容
// 匹配重复单词(如"hello hello")
Pattern pattern = Pattern.compile("(\\w+) \\1");
Matcher matcher = pattern.matcher("hello hello world");
if (matcher.find()) {
System.out.println("重复单词: " + matcher.group()); // hello hello
}
四、高级特性
1. 贪婪与非贪婪匹配
- 贪婪匹配:数量词(如
*、+)默认尽可能多地匹配 - 非贪婪匹配:在数量词后加
?,改为尽可能少地匹配
String text = "<html><body><h1>标题</h1></body></html>";
// 贪婪匹配(默认)
Pattern greedyPattern = Pattern.compile("<.*>");
Matcher greedyMatcher = greedyPattern.matcher(text);
if (greedyMatcher.find()) {
System.out.println("贪婪匹配: " + greedyMatcher.group());
// 输出: <html><body><h1>标题</h1></body></html>
}
// 非贪婪匹配
Pattern nonGreedyPattern = Pattern.compile("<.*?>");
Matcher nonGreedyMatcher = nonGreedyPattern.matcher(text);
while (nonGreedyMatcher.find()) {
System.out.println("非贪婪匹配: " + nonGreedyMatcher.group());
// 输出: <html>、<body>、<h1>、</h1>、</body>、</html>
}
2. 零宽断言
零宽断言用于在特定位置进行匹配检查,但不消耗字符。
(1)正向预查(Positive Lookahead)
- 语法:
(?=pattern) - 作用:匹配位置后面必须跟着
pattern
// 匹配密码(至少8位,包含数字)
Pattern pattern = Pattern.compile("^(?=.*\\d).{8,}$");
Matcher matcher = pattern.matcher("abc12345");
System.out.println(matcher.matches()); // true
(2)负向预查(Negative Lookahead)
- 语法:
(?!pattern) - 作用:匹配位置后面不能跟着
pattern
// 匹配不包含"cat"的字符串
Pattern pattern = Pattern.compile("^(?!.*cat).*$");
Matcher matcher = pattern.matcher("dog");
System.out.println(matcher.matches()); // true
(3)正向后行(Positive Lookbehind)
- 语法:
(?<=pattern) - 作用:匹配位置前面必须是
pattern
// 匹配美元价格(如$100)
Pattern pattern = Pattern.compile("(?<=\\$)\\d+");
Matcher matcher = pattern.matcher("价格:$100");
if (matcher.find()) {
System.out.println("金额: " + matcher.group()); // 100
}
3. 标志位(Pattern Flags)
通过
Pattern.compile() 的第二个参数设置标志位,影响匹配行为。// 使用CASE_INSENSITIVE忽略大小写
Pattern pattern = Pattern.compile("java", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher("Java Programming");
System.out.println(matcher.find()); // true
// 多行模式(^和$匹配每行的开始和结束)
Pattern multiLinePattern = Pattern.compile("^Hello", Pattern.MULTILINE);
Matcher multiLineMatcher = multiLinePattern.matcher("Hi\nHello World");
System.out.println(multiLineMatcher.find()); // true
五、字符串替换与分割
1. 替换方法
replaceAll(String replacement):替换所有匹配项replaceFirst(String replacement):替换第一个匹配项appendReplacement(StringBuffer sb, String replacement):自定义替换逻辑
String text = "Hello 123 World 456";
// 替换所有数字为X
String result = text.replaceAll("\\d", "X");
System.out.println(result); // Hello XXX World XXX
// 使用appendReplacement实现复杂替换
Pattern pattern = Pattern.compile("\\d+");
Matcher matcher = pattern.matcher(text);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
int num = Integer.parseInt(matcher.group()) * 2;
matcher.appendReplacement(sb, String.valueOf(num));
}
matcher.appendTail(sb);
System.out.println(sb.toString()); // Hello 246 World 912
2. 字符串分割
String.split() 方法底层使用正则表达式实现分割:String text = "a,b;c|d";
// 使用多种分隔符分割
String[] parts = text.split("[,;|]");
for (String part : parts) {
System.out.println(part); // 输出: a, b, c, d
}
六、性能优化与最佳实践
1. 性能优化建议
- 预编译 Pattern:避免在循环中重复编译正则表达式
- 使用非捕获分组:减少不必要的捕获,提高效率
- 优先使用
startsWith()/endsWith():简单匹配场景比正则更高效 - 避免复杂正则:过度复杂的正则表达式会显著降低性能
2. 常见陷阱
- 量词滥用:如
.*可能导致回溯爆炸 - 忽略边界:忘记使用
^和$可能导致意外匹配 - 错误转义:Java 字符串中
\需要转义为\\
3. 工具推荐
- RegexBuddy:强大的正则表达式调试工具
- IDE 辅助:IntelliJ IDEA 等 IDE 提供正则表达式可视化功能
总结
Java 正则表达式通过
Pattern 和 Matcher 类提供了灵活且强大的文本处理能力。掌握基本语法(字符类、数量词、分组)、核心方法(matches()、find())以及高级特性(零宽断言、标志位),能够应对各种复杂的文本匹配、查找和替换需求。在实际应用中,需注意性能优化和常见陷阱,合理使用正则表达式,避免过度复杂的模式定义。通过不断练习和实践,正则表达式将成为你处理文本数据的得力工具
浙公网安备 33010602011771号