Java正则表达式完全指南

发布于:2025-06-03 ⋅ 阅读:(21) ⋅ 点赞:(0)

正则表达式(Regular Expression,简称Regex)是一种强大的文本处理工具,它可以帮助开发者高效地进行字符串匹配、查找、替换和分割等操作。在Java中正则表达式的应用场景极为广泛,从简单的表单验证到复杂的文本解析,都离不开正则表达式的支持。虽然之前我也讲过正则表达式,但过于通用,今天本文将专门全面介绍Java中正则表达式的相关知识,从基础语法到高级应用,并结合丰富实例代码,带你深入理解和掌握这一强大工具。

一、正则表达式基础概念

1.1 什么是正则表达式

正则表达式是一种由字符和特殊符号组成的模式,用于描述字符串的特定格式规则。通过使用正则表达式,可以:

  • 检查字符串是否符合特定格式(如邮箱、手机号)
  • 从文本中提取感兴趣的内容(如URL、数字)
  • 替换文本中的特定部分
  • 将文本按特定规则分割

1.2 Java中的正则表达式支持

Java通过java.util.regex包提供对正则表达式的支持,主要涉及以下三个类:

  • Pattern:用于编译正则表达式,将正则表达式字符串编译为模式对象。
  • Matcher:用于执行匹配操作,对输入字符串进行解释和匹配操作。
  • PatternSyntaxException:用于处理正则表达式语法错误的异常类。

二、正则表达式基本语法

2.1 普通字符

普通字符包括没有显式指定为元字符的所有可打印和不可打印字符,它们直接匹配自身。例如:

  • abc 匹配字符串 “abc”
  • 123 匹配字符串 “123”

2.2 元字符

元字符是正则表达式中具有特殊含义的字符,常用的元字符及其含义如下:

元字符 描述
. 匹配除换行符以外的任意字符
^ 匹配字符串的开始位置
$ 匹配字符串的结束位置
* 匹配前面的子表达式零次或多次
+ 匹配前面的子表达式一次或多次
? 匹配前面的子表达式零次或一次
{n} 匹配前面的子表达式恰好n次
{n,} 匹配前面的子表达式至少n次
{n,m} 匹配前面的子表达式至少n次,至多m次
[] 匹配方括号中指定的任意一个字符
[^] 匹配不在方括号中指定的任意一个字符
() 标记一个子表达式的开始和结束位置
| 表示或关系,匹配两个或多个选项之一

2.3 预定义字符类

为了简化常用字符类的定义,Java提供了一些预定义字符类:

预定义字符类 等价表达式 描述
\d [0-9] 匹配一个数字字符
\D [^0-9] 匹配一个非数字字符
\w [a-zA-Z_0-9] 匹配一个单词字符(字母、数字、下划线)
\W [^a-zA-Z_0-9] 匹配一个非单词字符
\s [ \t\n\x0B\f\r] 匹配一个空白字符(空格、制表符、换行符等)
\S [^ \t\n\x0B\f\r] 匹配一个非空白字符

三、Java中正则表达式的基本用法

3.1 编译正则表达式

在Java中使用正则表达式,首先需要将正则表达式字符串编译为Pattern对象:

import java.util.regex.Pattern;

public class RegexExample {
    public static void main(String[] args) {
        // 编译正则表达式
        Pattern pattern = Pattern.compile("a.*c");
    }
}

3.2 创建Matcher对象并执行匹配

编译后的Pattern对象用于创建Matcher对象,然后通过Matcher对象执行匹配操作:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexExample {
    public static void main(String[] args) {
        // 编译正则表达式
        Pattern pattern = Pattern.compile("a.*c");
        
        // 创建Matcher对象
        Matcher matcher = pattern.matcher("abc");
        
        // 执行匹配操作
        boolean isMatch = matcher.matches();
        System.out.println("是否匹配: " + isMatch);  // 输出: true
    }
}

3.3 常用的Matcher方法

  • matches():尝试将整个输入序列与模式匹配。
  • find():在输入序列中查找下一个匹配的子序列。
  • group():返回当前匹配的子序列。
  • start():返回当前匹配的子序列的起始索引。
  • end():返回当前匹配的子序列的结束索引加1。

示例代码:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexExample {
    public static void main(String[] args) {
        String input = "Hello, world! Hello, Java!";
        Pattern pattern = Pattern.compile("Hello");
        Matcher matcher = pattern.matcher(input);
        
        // 查找所有匹配项
        while (matcher.find()) {
            System.out.println("匹配到: " + matcher.group() + 
                              ", 起始位置: " + matcher.start() + 
                              ", 结束位置: " + matcher.end());
        }
    }
}

输出结果:

匹配到: Hello, 起始位置: 0, 结束位置: 5
匹配到: Hello, 起始位置: 14, 结束位置: 19

四、正则表达式高级应用

4.1 分组与捕获

使用圆括号()可以将正则表达式中的部分内容分组,每个分组可以被单独捕获和引用。分组编号从1开始,0表示整个匹配结果。

示例:匹配并提取邮箱地址中的用户名和域名

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GroupExample {
    public static void main(String[] args) {
        String email = "test.user@example.com";
        String regex = "([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})";
        
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(email);
        
        if (matcher.matches()) {
            System.out.println("完整匹配: " + matcher.group(0));  // 整个匹配结果
            System.out.println("用户名: " + matcher.group(1));    // 第一组
            System.out.println("域名: " + matcher.group(2));      // 第二组
        }
    }
}

输出结果:

完整匹配: test.user@example.com
用户名: test.user
域名: example.com

4.2 反向引用

在正则表达式中,可以使用\n(n为数字)引用前面已经捕获的分组。例如,匹配重复的单词:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BackreferenceExample {
    public static void main(String[] args) {
        String text = "hello hello world world";
        String regex = "\\b(\\w+)\\s+\\1\\b";
        
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(text);
        
        while (matcher.find()) {
            System.out.println("重复的单词: " + matcher.group());
        }
    }
}

输出结果:

重复的单词: hello hello
重复的单词: world world

4.3 贪婪匹配与非贪婪匹配

  • 贪婪匹配:默认情况下,正则表达式的量词(如*+{n,m})是贪婪的,会尽可能多地匹配字符。
  • 非贪婪匹配:在量词后面加上?,可以将贪婪匹配转换为非贪婪匹配,尽可能少地匹配字符。

示例:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GreedyVsNonGreedy {
    public static void main(String[] args) {
        String text = "<html><body><h1>Hello</h1></body></html>";
        
        // 贪婪匹配
        String greedyRegex = "<.*>";
        Pattern greedyPattern = Pattern.compile(greedyRegex);
        Matcher greedyMatcher = greedyPattern.matcher(text);
        
        if (greedyMatcher.find()) {
            System.out.println("贪婪匹配: " + greedyMatcher.group());
        }
        
        // 非贪婪匹配
        String nonGreedyRegex = "<.*?>";
        Pattern nonGreedyPattern = Pattern.compile(nonGreedyRegex);
        Matcher nonGreedyMatcher = nonGreedyPattern.matcher(text);
        
        while (nonGreedyMatcher.find()) {
            System.out.println("非贪婪匹配: " + nonGreedyMatcher.group());
        }
    }
}

输出结果:

贪婪匹配: <html><body><h1>Hello</h1></body></html>
非贪婪匹配: <html>
非贪婪匹配: <body>
非贪婪匹配: <h1>
非贪婪匹配: </h1>
非贪婪匹配: </body>
非贪婪匹配: </html>

4.4 零宽断言

零宽断言用于在特定位置匹配某些内容,但不包含匹配的内容本身。Java支持四种零宽断言:

断言类型 语法 描述
正向先行断言 (?=pattern) 匹配后面跟着pattern的位置
负向先行断言 (?!pattern) 匹配后面不跟着pattern的位置
正向后行断言 (?<=pattern) 匹配前面是pattern的位置
负向后行断言 (?<!pattern) 匹配前面不是pattern的位置

示例:匹配所有以"ing"结尾的单词

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class LookaroundExample {
    public static void main(String[] args) {
        String text = "running jumping swimming";
        String regex = "\\b\\w+(?=ing\\b)";
        
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(text);
        
        while (matcher.find()) {
            System.out.println("匹配到: " + matcher.group());
        }
    }
}

输出结果:

匹配到: run
匹配到: jump
匹配到: swim

五、正则表达式在实际开发中的应用

5.1 表单验证

正则表达式常用于表单验证,确保用户输入的数据符合预期格式。

邮箱验证
public static boolean isValidEmail(String email) {
    String regex = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
    return Pattern.matches(regex, email);
}
手机号验证
public static boolean isValidPhone(String phone) {
    String regex = "^1[3-9]\\d{9}$";
    return Pattern.matches(regex, phone);
}
身份证号码验证
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}[0-9Xx]$";
    return Pattern.matches(regex, idCard);
}

5.2 文本替换

使用正则表达式可以方便地进行文本替换操作。

替换HTML标签
public static String removeHtmlTags(String html) {
    String regex = "<[^>]+>";
    return html.replaceAll(regex, "");
}
敏感词过滤
public static String filterSensitiveWords(String text) {
    String[] sensitiveWords = {"敏感词1", "敏感词2", "敏感词3"};
    String regex = String.join("|", sensitiveWords);
    return text.replaceAll(regex, "***");
}

5.3 文本分割

使用正则表达式可以按复杂规则分割文本。

按逗号或空格分割
public static String[] splitText(String text) {
    String regex = "[,\\s]+";
    return text.split(regex);
}
按数字分割
public static String[] splitByNumbers(String text) {
    String regex = "\\d+";
    return text.split(regex);
}

六、正则表达式性能优化

  1. 编译一次,多次使用:避免在循环中重复编译相同的正则表达式,应将编译后的Pattern对象缓存并复用。
  2. 简化正则表达式:复杂的正则表达式会降低匹配效率,尽量使用简单、明确的表达式。
  3. 避免过度使用回溯:贪婪匹配和反向引用可能导致大量回溯,影响性能。
  4. 优先使用String类的方法:对于简单的字符串操作,如startsWith()endsWith()indexOf()等,应优先使用String类的方法,比正则表达式效率更高。

七、常见问题与注意事项

  1. 转义字符问题:在Java字符串中使用正则表达式时,需要注意转义字符。例如,匹配点号.需要写成\\.,匹配反斜杠\需要写成\\\\
  2. 性能问题:复杂的正则表达式可能导致性能问题,特别是在处理大量数据时。
  3. 边界问题:使用^$时要注意是否需要匹配整个字符串,还是只需要匹配部分内容。
  4. Unicode支持:Java默认支持Unicode字符,但在处理非ASCII字符时需要特别注意。

总结

正则表达式是Java中强大的文本处理工具,掌握正则表达式的基本语法和Java中的使用方法,对于提高字符串处理效率和开发质量至关重要。本文从基础概念入手,详细介绍了正则表达式的语法、Java中的API使用、高级应用场景以及性能优化等方面的内容,希望你在今后熟练使用尽量掌握。

若这篇内容帮到你,动动手指支持下!关注不迷路,干货持续输出!
ヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノ


网站公告

今日签到

点亮在社区的每一天
去签到