Java 正则表达式详解
一、正则表达式基础
正则表达式(Regular Expression,简称 Regex)是一种描述字符串模式的工具,用于搜索、匹配和操作文本。在 Java 中,主要通过 java.util.regex
包中的 Pattern
和 Matcher
类实现正则表达式功能。
二、正则表达式语法
- 元字符:
.
:匹配除换行符以外的任意单个字符。^
:匹配输入字符串的开始位置。$
:匹配输入字符串的结束位置。*
:匹配前面的子表达式零次或多次。+
:匹配前面的子表达式一次或多次。?
:匹配前面的子表达式零次或一次。{n}
:匹配前面的子表达式恰好 n 次。{n,}
:匹配前面的子表达式至少 n 次。{n,m}
:匹配前面的子表达式至少 n 次,至多 m 次。
- 字符类:
[abc]
:匹配 a、b 或 c 中的任意一个字符。[^abc]
:匹配除 a、b、c 外的任意一个字符。[a-z]
:匹配小写字母 a 到 z。[A-Z]
:匹配大写字母 A 到 Z。[0-9]
:匹配数字 0 到 9。[a-zA-Z0-9]
:匹配字母或数字。
- 预定义字符集:
\\d
:匹配一个数字字符,等价于[0-9]
。\\D
:匹配一个非数字字符,等价于[^0-9]
。\\s
:匹配任何空白字符,包括空格、制表符、换页符等。\\S
:匹配任何非空白字符。\\w
:匹配包括下划线的任何字母、数字或下划线字符,等价于[a-zA-Z0-9_]
。\\W
:匹配任何非单词字符。
- 边界匹配:
\\b
:匹配单词边界,即单词字符(如字母、数字或下划线)和非单词字符之间的位置。\\B
:匹配非单词边界。
- 分组与捕获:
()
:捕获括号内的表达式,形成一个分组。|
:匹配左边或右边的表达式。
- 转义字符:
\\
:用于转义特殊字符,使其失去特殊含义。在 Java 中,正则表达式中的反斜杠需要写成\\\\
,因为 Java 字符串本身也需要转义。
三、Java 中的正则表达式类
Pattern
类:
- 表示编译后的正则表达式模式。
- 没有公共构造方法,需通过静态方法
compile(String regex)
编译正则表达式。 - 示例:
java Pattern pattern = Pattern.compile("\\d+"); // 编译匹配一个或多个数字的正则表达式
Matcher
类:
- 用于对输入字符串进行匹配操作。
- 通过调用
Pattern
对象的matcher(CharSequence input)
方法获取Matcher
对象。 - 常用方法:
find()
:尝试在输入字符串中查找下一个匹配的子字符串。matches()
:尝试将整个输入字符串与正则表达式进行匹配。group()
:返回上一次匹配操作所匹配的子字符串。replaceAll(String replacement)
:将输入字符串中所有匹配的子字符串替换为指定的字符串。
- 示例:
java Matcher matcher = pattern.matcher("abc123def456ghi"); while (matcher.find()) { System.out.println("找到匹配项: " + matcher.group()); // 输出: 找到匹配项: 123 找到匹配项: 456 }
四、正则表达式在 Java 中的常见应用
- 验证格式:
- 邮箱验证:
java String regex = "^[\\w.%+-]+@[\\w.-]+\\.[a-zA-Z]{2,}$"; String email = "test@example.com"; boolean isValid = email.matches(regex); // true
- 电话号码验证:
java String regex = "^\\d{3}-\\d{3}-\\d{4}$"; String phone = "123-456-7890"; boolean isValid = phone.matches(regex); // true
- 提取信息:
- 提取数字:
java String text = "订单123,金额456.78元"; Pattern pattern = Pattern.compile("\\d+\\.?\\d*"); Matcher matcher = pattern.matcher(text); while (matcher.find()) { System.out.println("找到数字: " + matcher.group()); // 输出: 找到数字: 123 找到数字: 456.78 }
- 替换文本:
- 日期格式转换:
java String text = "2023-05-15"; String newText = text.replaceAll("(\\d{4})-(\\d{2})-(\\d{2})", "$2/$3/$1"); // 结果: "05/15/2023"
- 分割字符串:
- 按数字分割:
java Pattern pattern = Pattern.compile("\\d+"); String input = "abc123def456ghi"; String[] parts = pattern.split(input); // 结果: ["abc", "def", "ghi"]
五、正则表达式优化技巧
- 使用非贪婪匹配:
- 贪婪匹配会尽可能多地匹配字符,而非贪婪匹配则尽可能少地匹配字符。
- 示例:
- 贪婪匹配:
<.*>
(匹配<html>...</html>
为一个整体) - 非贪婪匹配:
<.*?>
(匹配<html>
和</html>
分别)
- 贪婪匹配:
- 使用字符类和预定义字符集:
- 字符类(如
[a-z]
)和预定义字符集(如\\d
)比使用单独的字符或字符串更高效。 - 示例:
- 高效:
\\d{3}-\\d{2}-\\d{4}
- 低效:
[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]
- 高效:
- 减少回溯:
- 复杂的正则表达式可能导致大量的回溯,特别是当表达式中包含多个嵌套量词时。减少回溯可以显著提高匹配效率。
- 示例:
- 低效:
(a+)+b
- 高效:
a+b
- 低效:
- 编译正则表达式:
- 将正则表达式编译为
Pattern
对象,并重用它,而不是每次匹配时都编译。 - 示例:
java Pattern pattern = Pattern.compile("\\b\\w+\\b"); // 编译一次 Matcher matcher = pattern.matcher("Some text here."); while (matcher.find()) { System.out.println("Found word: " + matcher.group()); }
- 使用
String
类的内置方法:
- 对于一些简单的匹配任务,
String
类的内置方法(如contains()
、startsWith()
和endsWith()
)通常比正则表达式更高效。 - 示例:
java String text = "Hello, world!"; boolean containsWorld = text.contains("world"); // true
六、正则表达式示例代码
- 验证用户名:
- 用户名规则:长度 3-15,由字母、数字、下划线、连字符组成。
- 正则表达式:
^[a-z0-9_-]{3,15}$
- 示例:
java String regex = "^[a-z0-9_-]{3,15}$"; String username = "user_name-123"; boolean isValid = username.matches(regex); // true
- 验证密码:
- 密码规则:长度 6-20,包含至少一个数字、一个小写字母、一个大写字母和一个特殊字符(如
@#$%
)。 - 正则表达式:
((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{6,20})
- 示例:
java String regex = "((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%]).{6,20})"; String password = "Pass@123"; boolean isValid = password.matches(regex); // true
- 提取 HTML 链接:
- 示例:
java String html = "<html><body><a href='https://example.com'>Example</a></body></html>"; String regex = "href=['\"](.*?)['\"]"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(html); while (matcher.find()) { System.out.println(matcher.group(1)); // 输出: https://example.com }
七、总结
- 正则表达式是 Java 中用于模式匹配和字符串处理的强大工具。
- 核心类:
Pattern
:编译正则表达式。Matcher
:执行匹配操作。
- 常见应用:
- 验证格式(如邮箱、电话号码)。
- 提取信息(如数字、链接)。
- 替换文本(如日期格式转换)。
- 分割字符串(如按数字分割)。
- 优化技巧:
- 使用非贪婪匹配。
- 使用字符类和预定义字符集。
- 减少回溯。
- 编译正则表达式并重用。
- 对于简单任务,使用
String
类的内置方法。