Java正则表达式详解
Java正则表达式详解
什么是正则表达式?
正则表达式是一种用于匹配字符串中字符组合的模式。在Java中,正则表达式是通过java.util.regex包中的类实现的。
正则表达式的用途
- 字符串匹配:检查字符串是否符合特定模式
- 字符串查找:在字符串中查找符合特定模式的子串
- 字符串替换:替换字符串中符合特定模式的部分
- 字符串分割:根据特定模式分割字符串
- 表单验证:验证用户输入是否符合要求
正则表达式的语法
1. 字符类
| 表达式 | 描述 |
|---|---|
[abc] | 匹配方括号内的任意一个字符 |
[^abc] | 匹配除了方括号内的任意一个字符 |
[a-z] | 匹配a到z范围内的任意一个字符 |
[A-Z] | 匹配A到Z范围内的任意一个字符 |
[0-9] | 匹配0到9范围内的任意一个字符 |
[a-zA-Z] | 匹配a到z或A到Z范围内的任意一个字符 |
[a-zA-Z0-9] | 匹配a到z、A到Z或0到9范围内的任意一个字符 |
2. 预定义字符类
| 表达式 | 描述 |
|---|---|
. | 匹配任意一个字符(除了换行符) |
\d | 匹配一个数字字符,等价于[0-9] |
\D | 匹配一个非数字字符,等价于[^0-9] |
\w | 匹配一个单词字符(字母、数字、下划线),等价于[a-zA-Z0-9_] |
\W | 匹配一个非单词字符,等价于[^a-zA-Z0-9_] |
\s | 匹配一个空白字符(空格、制表符、换行符等) |
\S | 匹配一个非空白字符 |
3. 边界匹配器
| 表达式 | 描述 |
|---|---|
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
\b | 匹配单词边界 |
\B | 匹配非单词边界 |
4. 量词
| 表达式 | 描述 |
|---|---|
* | 匹配前面的表达式0次或多次,等价于{0,} |
+ | 匹配前面的表达式1次或多次,等价于{1,} |
? | 匹配前面的表达式0次或1次,等价于{0,1} |
{n} | 匹配前面的表达式恰好n次 |
{n,} | 匹配前面的表达式至少n次 |
{n,m} | 匹配前面的表达式至少n次,最多m次 |
5. 贪婪与非贪婪
| 表达式 | 描述 |
|---|---|
*? | 非贪婪匹配前面的表达式0次或多次 |
+? | 非贪婪匹配前面的表达式1次或多次 |
?? | 非贪婪匹配前面的表达式0次或1次 |
{n,m}? | 非贪婪匹配前面的表达式至少n次,最多m次 |
6. 逻辑运算符
| 表达式 | 描述 |
|---|---|
| ` | ` |
(xyz) | 捕获组,匹配括号内的表达式并捕获匹配项 |
(?:xyz) | 非捕获组,匹配括号内的表达式但不捕获匹配项 |
(?<name>xyz) | 命名捕获组,匹配括号内的表达式并以指定名称捕获匹配项 |
7. 特殊字符
| 表达式 | 描述 |
|---|---|
\ | 转义字符,用于匹配特殊字符本身 |
| ` | ` |
() | 捕获组 |
[] | 字符类 |
{} | 量词 |
^ | 字符串开始 |
$ | 字符串结束 |
. | 任意字符 |
* | 0次或多次 |
+ | 1次或多次 |
? | 0次或1次 |
Java中使用正则表达式
1. Pattern和Matcher类
Java中使用Pattern和Matcher类来处理正则表达式。
import java.util.regex.Matcher;import java.util.regex.Pattern;
public class RegexDemo { public static void main(String[] args) { // 正则表达式模式 String regex = "\\d{3}-\\d{3}-\\d{4}"; // 匹配电话号码格式
// 要匹配的字符串 String text = "我的电话号码是123-456-7890,你的电话号码是987-654-3210。";
// 编译正则表达式 Pattern pattern = Pattern.compile(regex);
// 创建Matcher对象 Matcher matcher = pattern.matcher(text);
// 查找所有匹配项 while (matcher.find()) { System.out.println("找到匹配项: " + matcher.group()); System.out.println("开始位置: " + matcher.start()); System.out.println("结束位置: " + matcher.end()); } }}2. String类中的正则表达式方法
String类提供了一些使用正则表达式的方法:
2.1 matches()
检查字符串是否完全匹配正则表达式。
String email = "user@example.com";String emailRegex = "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}";boolean isValid = email.matches(emailRegex);System.out.println("邮箱是否有效: " + isValid);2.2 split()
根据正则表达式分割字符串。
String text = "apple,banana,orange";String[] fruits = text.split(",");for (String fruit : fruits) { System.out.println(fruit);}2.3 replaceAll()
替换字符串中所有匹配正则表达式的部分。
String text = "Hello 123 World 456";String replaced = text.replaceAll("\\d+", "*");System.out.println("替换后: " + replaced); // 输出: Hello * World *2.4 replaceFirst()
替换字符串中第一个匹配正则表达式的部分。
String text = "Hello 123 World 456";String replaced = text.replaceFirst("\\d+", "*");System.out.println("替换后: " + replaced); // 输出: Hello * World 456正则表达式的应用示例
1. 验证邮箱
public static boolean isValidEmail(String email) { String regex = "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"; return email.matches(regex);}2. 验证电话号码
public static boolean isValidPhone(String phone) { String regex = "\\d{3}-\\d{3}-\\d{4}"; return phone.matches(regex);}3. 验证身份证号
public static boolean isValidIdCard(String idCard) { String regex = "[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[\\dXx]"; return idCard.matches(regex);}4. 验证URL
public static boolean isValidUrl(String url) { String regex = "https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b([-a-zA-Z0-9()@:%_\\+.~#?&//=]*)", return url.matches(regex);}5. 提取HTML标签
public static List<String> extractHtmlTags(String html) { List<String> tags = new ArrayList<>(); String regex = "<([a-z][a-z0-9]*)\\b[^>]*>([\\s\\S]*?)<\\/\\1>"; Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(html); while (matcher.find()) { tags.add(matcher.group()); } return tags;}6. 替换空白字符
public static String replaceWhitespace(String text) { return text.replaceAll("\\s+", " ");}7. 提取数字
public static List<String> extractNumbers(String text) { List<String> numbers = new ArrayList<>(); String regex = "\\d+"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(text); while (matcher.find()) { numbers.add(matcher.group()); } return numbers;}8. 驼峰命名转换为下划线命名
public static String camelToSnake(String camelCase) { return camelCase.replaceAll("([a-z0-9])([A-Z])", "$1_$2").toLowerCase();}9. 下划线命名转换为驼峰命名
public static String snakeToCamel(String snakeCase) { return Pattern.compile("_([a-z])").matcher(snakeCase).replaceAll(m -> m.group(1).toUpperCase());}10. 验证密码强度
public static boolean isStrongPassword(String password) { // 至少8个字符,包含至少一个大写字母、一个小写字母、一个数字和一个特殊字符 String regex = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$"; return password.matches(regex);}正则表达式的最佳实践
1. 编译正则表达式
对于频繁使用的正则表达式,应该编译成Pattern对象并缓存起来,以提高性能。
// 缓存Pattern对象private static final Pattern EMAIL_PATTERN = Pattern.compile("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}");
public static boolean isValidEmail(String email) { return EMAIL_PATTERN.matcher(email).matches();}2. 使用非捕获组
对于不需要捕获的组,使用非捕获组(?:...)可以提高性能。
// 捕获组Pattern pattern1 = Pattern.compile("(abc|def)");
// 非捕获组(更高效)Pattern pattern2 = Pattern.compile("(?:abc|def)");3. 避免回溯
复杂的正则表达式可能会导致回溯,影响性能。应该:
- 避免使用嵌套量词
- 避免使用贪婪量词
- 使用更具体的模式
4. 测试正则表达式
在使用正则表达式之前,应该使用各种测试用例进行测试,确保它能正确匹配预期的字符串,同时不匹配非预期的字符串。
5. 文档化正则表达式
复杂的正则表达式应该添加注释,说明其用途和工作原理。
/** * 验证邮箱格式 * 规则: * 1. 用户名部分:字母、数字、点、下划线、百分号、加号、减号 * 2. @符号 * 3. 域名部分:字母、数字、点、减号 * 4. 顶级域名:至少2个字母 */private static final Pattern EMAIL_PATTERN = Pattern.compile("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}");6. 处理转义字符
在Java字符串中,反斜杠是转义字符,因此在正则表达式中使用反斜杠时,需要使用双反斜杠。
// 匹配一个数字String regex = "\\d"; // 第一个反斜杠是Java字符串的转义字符7. 考虑国际化
在处理国际化字符串时,应该考虑不同语言的字符集。
// 匹配任何语言的字母String regex = "\\p{L}+";常见陷阱
1. 贪婪匹配
默认情况下,量词是贪婪的,会尽可能多地匹配字符。这可能会导致意外的结果。
String text = "<div>内容1</div><div>内容2</div>";String regex = "<div>.*</div>";// 匹配结果:<div>内容1</div><div>内容2</div>(整个字符串)
// 使用非贪婪匹配String regexNonGreedy = "<div>.*?</div>";// 匹配结果:<div>内容1</div>(第一个div)2. 转义字符
在Java字符串中,反斜杠是转义字符,因此在正则表达式中使用反斜杠时,需要使用双反斜杠。
// 错误的做法String regex = "\d+"; // 这会被解释为\d+,但在Java字符串中,\d不是有效的转义序列
// 正确的做法String regex = "\\d+"; // 使用双反斜杠3. 性能问题
复杂的正则表达式可能会导致性能问题,特别是在处理长字符串时。
// 可能导致回溯的正则表达式String regex = "(a+)+b";String text = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";// 这会导致大量的回溯,可能会使程序崩溃4. 边界匹配器
忘记使用边界匹配器(如^和$)可能会导致部分匹配。
String text = "123-456-7890";String regex = "\\d{3}-\\d{3}-\\d{4}";// 正确匹配
String text2 = "123-456-7890abc";String regex2 = "\\d{3}-\\d{3}-\\d{4}";// 也会匹配,因为它只匹配了字符串的一部分
// 正确的做法:使用边界匹配器String regex3 = "^\\d{3}-\\d{3}-\\d{4}$";// 只会匹配完全符合格式的字符串5. 特殊字符
忘记转义特殊字符可能会导致正则表达式无法正常工作。
String text = "文件.txt";String regex = ".*\\.txt";// 正确匹配
String text2 = "文件(name).txt";String regex2 = ".*\(name\).*";// 需要转义括号总结
正则表达式是一种强大的工具,用于匹配、查找、替换和分割字符串。Java提供了Pattern和Matcher类来处理正则表达式,以及String类中的一些便捷方法。
本文介绍了正则表达式的基本语法、Java中的使用方法、应用示例和最佳实践。希望本文能够帮助你更好地理解和使用正则表达式。
练习
-
编写一个程序,使用正则表达式验证邮箱格式。
-
编写一个程序,使用正则表达式验证电话号码格式。
-
编写一个程序,使用正则表达式验证身份证号格式。
-
编写一个程序,使用正则表达式提取字符串中的所有数字。
-
编写一个程序,使用正则表达式替换字符串中的空白字符为单个空格。
-
编写一个程序,使用正则表达式将驼峰命名转换为下划线命名。
-
编写一个程序,使用正则表达式将下划线命名转换为驼峰命名。
-
编写一个程序,使用正则表达式验证密码强度。
-
编写一个程序,使用正则表达式提取HTML标签。
-
编写一个程序,使用正则表达式验证URL格式。
通过这些练习,你将更加熟悉正则表达式的使用,为后续的学习做好准备。
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!