Java 正则表达式学习笔记(详细版)

正则表达式(Regular Expression,简称 Regex)是一种强大的文本模式匹配工具。在 Java 中,正则表达式通过 java.util.regex 包实现,主要用于字符串匹配、查找、替换和分割。


一、核心类与流程

Java 处理正则表达式主要涉及三个类:

  1. Pattern:正则表达式的编译表示。
    • 正则表达式字符串首先被编译成 Pattern 对象。
    • 最佳实践:如果正则表达式会被重复使用,应将其编译为 Pattern 对象并缓存(static final),避免重复编译带来的性能开销。
  2. Matcher:执行匹配操作的对象。
    • 由 Pattern 对象创建,用于对目标字符串进行匹配、查找和替换。
  3. PatternSyntaxException:当正则表达式语法错误时抛出的异常。

标准使用流程

// 1. 编译正则表达式
Pattern pattern = Pattern.compile("\\d+");

// 2. 创建匹配器
Matcher matcher = pattern.matcher("abc123def456");

// 3. 执行匹配操作
while (matcher.find()) {
    System.out.println("找到: " + matcher.group());
}

二、常用元字符与语法

在 Java 字符串中,反斜杠 \ 是转义字符,因此正则表达式中的 \ 需要写成 \\。

1. 字符匹配

符号含义示例匹配内容
.匹配任意单个字符(除换行符外)a.cabc, a2c
\d匹配数字\d+123, 0
\D匹配非数字\D+abc, !
\w匹配单词字符(字母、数字、下划线)\w+user_name, a1
\W匹配非单词字符\W+!@#,
\s匹配空白字符(空格、制表符、换行)\s+ , \t
\S匹配非空白字符\S+abc
[abc]匹配括号内的任意一个字符[a-z]a, z
[^abc]匹配非括号内的字符[^0-9]a, !
[a-z]匹配范围内的字符[a-zA-Z]a, Z

2. 数量限定符

符号含义示例匹配次数
*0 次或多次ab*cac, abc, abbbc
+1 次或多次ab+cabc, abbbc (不匹配 ac)
?0 次或 1 次colou?rcolor, colour
{n}恰好 n 次\d{3}123
{n,}至少 n 次\d{3,}123, 12345
{n,m}n 到 m 次\d{2,4}12, 1234

注意:默认是贪婪匹配(尽可能多匹配)。

  • * 是贪婪的,*? 是非贪婪(懒惰)匹配。
  • 示例:a.*b 匹配 aabab 会匹配整个字符串;a.*?b 会匹配 aab 和 ab。

3. 边界匹配

符号含义示例
^匹配字符串开头^abc (以 abc 开头)
$匹配字符串结尾xyz$ (以 xyz 结尾)
\b匹配单词边界\bcat\b (匹配独立的 cat)
\B匹配非单词边界\Bcat\B

4. 分组与引用

  • ():捕获分组。将括号内的内容作为一个整体,并可以后续引用。
  • (?:...):非捕获分组。只分组,不捕获(不占用组号,性能稍好)。
  • \1, \2:反向引用。引用前面第 1、2 个分组匹配的内容。
  • |:或。匹配左边或右边。

示例:

// 匹配重复的单词
String regex = "(\\w+)\\s+\\1"; 
// 匹配 "hello hello",但不匹配 "hello world"

三、常用方法详解

1. Pattern 类的方法

  • static Pattern compile(String regex):编译正则。
  • static Pattern compile(String regex, int flags):带标志位编译(如 CASE_INSENSITIVE 忽略大小写)。
  • Matcher matcher(CharSequence input):创建匹配器。

2. Matcher 类的方法

方法功能返回值说明
matches()判断整个字符串是否匹配boolean必须完全匹配
lookingAt()判断开头是否匹配boolean只要开头匹配即可
find()查找下一个匹配项boolean可循环调用查找所有
group()返回当前匹配的文本String默认返回第 0 组(整体)
group(int i)返回第 i 个捕获组String从 1 开始计数
start() / end()返回匹配项的起始/结束索引int
replaceAll(String repl)替换所有匹配项String返回新字符串
replaceFirst(String repl)替换第一个匹配项String
appendReplacement() / appendTail()高级替换(支持动态逻辑)Matcher用于复杂替换场景

四、实战代码示例

1. 验证邮箱格式

public static boolean isValidEmail(String email) {
    // 简单邮箱正则:字母数字下划线 + @ + 域名 + . + 后缀
    String regex = "^[a-zA-Z0-9_+.-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
    return email != null && email.matches(regex);
}
// 注意:String.matches() 内部会自动编译 Pattern,适合一次性使用

2. 提取 HTML 标签中的内容

String html = "<div>Hello</div><p>World</p>";
String regex = "<(\\w+)>(.*?)</\\1>"; // \1 引用第一个分组 <div> 对应 </div>

Pattern pattern = Pattern.compile(regex, Pattern.DOTALL); // DOTALL 让 . 匹配换行符
Matcher matcher = pattern.matcher(html);

while (matcher.find()) {
    System.out.println("标签名: " + matcher.group(1)); // div, p
    System.out.println("内容: " + matcher.group(2));   // Hello, World
}

3. 替换敏感词

String text = "这个产品非常棒,但是价格有点贵。";
String regex = "贵";
String replacement = "性价比低";

String result = text.replaceAll(regex, replacement);
System.out.println(result); // 这个产品非常棒,但是价格有点性价比低。

4. 分割字符串

String data = "apple,banana;orange|grape";
// 使用正则分割,支持多种分隔符
String[] fruits = data.split("[,;|]"); 
for (String f : fruits) {
    System.out.println(f);
}

5. 性能优化(缓存 Pattern)

// ❌ 不推荐:每次调用都重新编译
public boolean check(String input) {
    return input.matches("\\d{3}-\\d{4}");
}

// ✅ 推荐:静态缓存 Pattern
private static final Pattern PHONE_PATTERN = Pattern.compile("\\d{3}-\\d{4}");

public boolean check(String input) {
    return PHONE_PATTERN.matcher(input).matches();
}

五、常用标志位 (Flags)

在 Pattern.compile(regex, flags) 中使用:

标志位含义
Pattern.CASE_INSENSITIVE忽略大小写
Pattern.MULTILINE多行模式,^ 和 $ 匹配每行的开头结尾
Pattern.DOTALL单行模式,. 可以匹配换行符 \n
Pattern.UNICODE_CASE配合 CASE_INSENSITIVE 支持 Unicode 大小写
Pattern.COMMENTS允许正则中包含注释和空白(需配合 # 注释)

示例:

Pattern p = Pattern.compile("abc", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);

六、常见陷阱与注意事项

1. 转义字符问题

Java 字符串中 \ 需要转义,正则中 \ 也需要转义。

  • 匹配反斜杠:正则写 \\,Java 字符串写 "\\\\"。
  • 匹配数字:正则写 \d,Java 字符串写 "\\d"。

2. 贪婪 vs 非贪婪

  • .* 是贪婪的,会匹配尽可能多的字符。
  • .*? 是非贪婪的,匹配尽可能少的字符。
  • 示例:<.*> 匹配 <a> <b> 会匹配整个字符串;<.*?> 会分别匹配 <a> 和 <b>。

3. matches() vs find()

  • matches():要求整个字符串完全匹配正则。
  • find():在字符串中查找是否存在匹配的子串。
  • 示例:"abc123".matches("\\d+") 返回 false;"abc123".find("\\d+") 返回 true。

4. 性能问题

  • 避免在循环中频繁编译正则。
  • 避免使用复杂的嵌套分组或回溯过多的正则(可能导致 Catastrophic Backtracking,程序卡死)。
  • 对于简单的分割或替换,优先使用 String 类的方法(如 split, replace),它们内部可能做了优化。

5. 特殊字符转义

在字符类 [] 中,大部分特殊字符不需要转义,但 ], \, ^, - 需要小心处理。

  • 匹配 -:放在 [] 开头或结尾,或转义 \\-。
  • 匹配 ]:必须转义 \\] 或放在 [] 开头。

七、总结

  • 核心类:Pattern (编译), Matcher (匹配)。
  • 语法:掌握 \d, \w, *, +, ?, (), | 等核心元字符。
  • 性能:重复使用的正则务必缓存 Pattern 对象。
  • 转义:注意 Java 字符串的双重转义。
  • 工具:善用 String.matches(), split(), replaceAll() 简化代码,但复杂逻辑用 Pattern/Matcher。

正则表达式是处理文本的利器,熟练掌握能极大提升开发效率。建议配合在线正则测试工具(如 regex101.com)进行练习。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐