Java正则表达式全攻略:从基础语法到高级应用
目录
前言
正则表达式(Regular Expression,简称Regex)是处理字符串的利器。在Java中,通过java.util.regex包提供了强大的支持。无论你是要进行数据验证、文本替换还是复杂的内容提取,掌握正则表达式都是程序员的必备技能。
一、Java正则的核心类
在Java中,我们主要通过两个类来操作正则表达式:
1.java.util.regex.Pattern:正则表达式的编译表示。一个Pattern对象就是一个编译好的正则表达式,它是线程安全的,可以被多个线程共享。
2.java.util.regex.Matcher:匹配器。它负责解释Pattern对象,并对目标字符串进行匹配操作。
基本使用流程:
1.编译:将字符串形式的正则表达式编译成Pattern对象。
2.匹配:通过Pattern对象创建Matcher对象,并传入待匹配的字符串。
3.操作:调用Matcher的方法(如find(), matches(), group())进行匹配、查找或替换。
二、正则表达式基础语法速查
正则表达式由普通字符(如a-z)和元字符(有特殊含义的字符)组成。
常用元字符表
| 元字符 | 描述 | 示例 |
| . | 匹配除换行符以外的任意字符 | a.c 匹配 "abc", "a2c" |
| ^ | 匹配字符串的开始 | ^Hello 匹配以 "Hello" 开头的字符串 |
| $ | 匹配字符串的结束 | world$ 匹配以 "world" 结尾的字符串 |
| * | 匹配前面的子表达式零次或多次 | ab*c 匹配 "ac", "abc", "abbbc" |
| + | 匹配前面的子表达式一次或多次 | ab+c 匹配 "abc", "abbbc",但不匹配 "ac" |
| ? | 匹配前面的子表达式零次或一次 | ab?c 匹配 "ac", "abc" |
| {n} | 匹配前面的子表达式恰好n次 | a{3} 匹配 "aaa" |
| {n,} | 匹配前面的子表达式至少n次 | a{2,} 匹配 "aa", "aaa", ... |
| {n,m} | 匹配前面的子表达式至少n次,至多m次 | a{2,4} 匹配 "aa", "aaa", "aaaa" |
常用预定义字符类
| 字符 | 等价于 | 描述 |
| \d | [0-9] | 匹配一个数字字符 |
| \D | [^0-9] | 匹配一个非数字字符 |
| \w | [a-zA-Z_0-9] | 匹配一个单词字符(字母、数字、下划线) |
| \W | [^\w] | 匹配一个非单词字符 |
| \s | [\t\n\x0B\f\r] | 匹配一个空白字符(空格、制表符、换行等) |
| \S | [^\s] | 匹配一个非空白字符 |
三、Java中的实战应用
1. 验证字符串(matches方法)
这是最常用的功能,比如验证手机号、邮箱、身份证等。
重要提示:String类的matches()方法会尝试匹配整个字符串。
// 验证手机号 (简单的11位数字)
String phone = "13800138000";
// 正则:以1开头,第二位是3-9,后面跟9位数字
String regex = "^1[3-9]\\d{9}$";
boolean isValid = phone.matches(regex);
System.out.println(isValid); // 输出: true
2. 查找内容(find与group方法)
如果你想在一段文本中提取所有符合规则的内容,需要用Pattern和Matcher。
String text = "联系方式:13800138000,备用电话:13912345678。";
String regex = "1[3-9]\\d{9}"; // 手机号正则
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
// group() 返回匹配到的子字符串
System.out.println("找到手机号: " + matcher.group());
}
// 输出:
// 找到手机号: 13800138000
// 找到手机号: 13912345678
3. 替换内容(replaceAll方法)
将文本中符合正则的部分替换成新的内容。
String text = "你的验证码是1234,请在5分钟内输入。";
// 将所有的数字替换为 *
String masked = text.replaceAll("\\d", "*");
System.out.println(masked);
// 输出: 你的验证码是****,请在*分钟内输入。
四、避坑指南与最佳实践
1. 转义字符的双重折磨
在Java字符串中,\本身就是一个转义字符。因此,要在Java代码中表示正则中的\d,你必须写成"\\d"。
1.正则:\d (匹配数字) -> Java字符串:"\\d"
2.正则:\. (匹配点号) -> Java字符串:"\\."
2. matches() vs find()
1.String.matches() / Matcher.matches():必须全匹配。如果正则只是字符串的一部分,它会返回false。
2.Matcher.find():部分匹配。只要在字符串中找到了符合正则的子串,就返回true。
3. 性能优化:复用Pattern
Pattern.compile()是一个相对耗时的操作。如果你的正则表达式是固定不变的(例如验证邮箱),建议将其定义为static final常量,以避免重复编译。
public class RegexUtils {
// 预编译,提升性能
private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
public static boolean isPhone(String input) {
if (input == null) return false;
return PHONE_PATTERN.matcher(input).matches();
}
}
五、常用元字符与预定义字符类
在写代码之前,必须熟记这些“积木”。
| 表达式 | 含义 | 备注 |
| \d | 匹配一个数字,等价于 [0-9] | 注意:Java字符串中需写成 "\\d" |
| \w | 匹配字母、数字或下划线,等价于 [a-zA-Z_0-9] | "Word" 字符 |
| \s | 匹配任意空白符,包括空格、制表符等 | "Space" |
| . | 匹配任意字符(换行符除外) | |
| ^ | 匹配行的开始 | 如 ^Hello |
| $ | 匹配行的结束 | 如 World$ |
| * | 匹配 0 次或多次 | 贪婪模式 |
| + | 匹配 1 次或多次 | 至少出现一次 |
| ? | 匹配 0 次或 1 次 | 也可用于非贪婪模式修饰符 |
| {n} | 匹配确定的 n 次 | 如 \d{11} 匹配11位手机号 |
| [] | 字符集合 | [abc] 匹配 a 或 b 或 c |
| ` | ` | 逻辑“或” |
特别注意:
在 Java 代码中编写正则时,反斜杠 \ 本身是 Java 字符串的转义字符。因此,如果你想表示正则中的 \d,在 Java 字符串中必须写成 "\\d"。这就是为什么你在之前的截图中看到 String re = "\\d";。
六、三大核心匹配模式
Matcher 类提供了三种主要的匹配方法,它们的区别非常重要:
1.matches():全量匹配
1.含义:尝试将整个输入序列与模式进行匹配。
2.场景:表单验证(如验证手机号是否完全符合规则)。
3.例子:正则 \d+ 对字符串 123abc 使用 matches() 会返回 false,因为后面有字母。
2.find():查找匹配
1.含义:扫描输入序列,查找下一个匹配的子序列。
2.场景:爬虫提取数据、日志分析(在一长串文本中找特定的IP或日期)。
3.例子:正则 \d+ 对字符串 123abc456 使用 find(),第一次找到 123,第二次找到 456。
3.lookingAt():前缀匹配
1.含义:尝试从输入序列的开头开始匹配,但不要求匹配整个序列。
2.场景:较少用,用于判断字符串是否以某种特定模式开头。
七、实战代码演示
让我们通过一个完整的工具类来看看如何使用正则。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexDemo {
public static void main(String[] args) {
// 1. 定义正则:匹配手机号(简单示例:1开头,第二位3-9,后面9位数字)
// 注意:Java字符串中需要双反斜杠
String regex = "^1[3-9]\\d{9}$";
// 2. 编译正则表达式
Pattern pattern = Pattern.compile(regex);
// 3. 创建匹配器
String phone = "13812345678";
Matcher matcher = pattern.matcher(phone);
// 4. 执行匹配
if (matcher.matches()) {
System.out.println(phone + " 是合法的手机号");
} else {
System.out.println(phone + " 格式错误");
}
// --- 进阶:提取内容 ---
String text = "联系方式:13800138000,备用号:13900139000";
String extractRegex = "1[3-9]\\d{9}"; // 去掉 ^ 和 $ 以便在文本中查找
Matcher extractMatcher = Pattern.compile(extractRegex).matcher(text);
System.out.println("开始提取手机号:");
// 使用 while 循环配合 find() 提取所有匹配项
while (extractMatcher.find()) {
// group() 获取匹配到的字符串
System.out.println("找到号码: " + extractMatcher.group());
}
}
}
八、常见坑点与解决方案
结合你之前的报错截图,这里有两个新手最容易踩的坑:
1. JDK 版本不兼容 (List.of 报错)
正如你在截图中遇到的 java: 找不到符号 错误:
原因:List.of() 是 Java 9 才引入的新特性。如果你的 IntelliJ IDEA 项目设置的是 Java 8(这是目前很多企业项目的标准),编译器就不认识这个方法。
解决:
1.方法A(推荐):升级项目的 JDK 版本到 11 或 17。
2.方法B(兼容写法):退回到 Java 8 的写法,使用 Arrays.asList:
// Java 9+ 写法
List.of("010-12345678", "020-99999999");
// Java 8 兼容写法
Arrays.asList("010-12345678", "020-99999999");
2. 贪婪 vs 非贪婪
1.贪婪模式(默认):正则表达式会尽可能多地匹配字符。
例如:字符串 <a>hello</a><b>world</b>,使用正则 <.*>。
结果:它会一次性匹配从第一个 < 到最后一个 > 的所有内容。
2.非贪婪模式:在量词后加 ?,尽可能少地匹配。
例如:正则 <.*?>。
结果:它会匹配到 </a> 就停止,分别得到 <a>hello</a> 和 <b>world</b>。
九、总结
正则表达式是 Java 开发中处理文本的“瑞士军刀”。
1.简单验证(如 String.matches())适合简单的逻辑。
2.复杂的提取和替换(如 Pattern + Matcher)适合处理大量数据。
3.务必注意 Java 转义字符(双反斜杠)和 JDK 版本 的兼容性。
希望这篇文章能帮你彻底搞懂 Java 正则!
更多推荐
所有评论(0)