Java正则表达式详解:从入门到实战,搞定文本处理难题

5 阅读7分钟

Java正则表达式详解:从入门到实战,搞定文本处理难题

在Java开发中,文本处理是高频场景——表单验证、日志提取、字符串清洗、数据解析等,都离不开正则表达式的支持。正则表达式(Regular Expression,简称Regex)本质是一套字符匹配的规则模板,用简洁的符号组合,就能快速实现“判断字符串是否符合规则”“提取符合条件的内容”“替换指定内容”等操作,极大提升开发效率。

本文将从Java正则的核心基础、核心类用法、实战场景、常见避坑点四个维度,结合具体代码示例,帮你快速掌握正则表达式,轻松应对各类文本处理需求,同时兼顾面试高频考点。

一、正则表达式核心基础:语法入门

正则表达式的核心是“符号组合”,这些符号分为「普通字符」和「元字符」,元字符是正则的精髓,也是实现灵活匹配的关键。需要特别注意:Java中所有带反斜杠\的正则符号,都要写成\\(因为Java字符串中\本身需要转义),这是初学者最易踩坑的点之一。

1.1 普通字符

普通字符即日常使用的字母、数字、汉字、普通符号(如a-z0-9@),正则中写什么,就匹配字符串中的什么,无特殊含义。

示例:正则abc,只能匹配字符串abc,不匹配ababcdaBc(正则默认区分大小写)。

1.2 核心元字符(必记)

元字符是具有特殊含义的符号,能匹配某一类符合条件的字符,无需逐个枚举,是正则简化书写的核心。以下按使用频率排序,记熟这些就能解决80%的日常需求:

元字符含义示例
.匹配任意1个字符(除了换行\n正则a.b可匹配a1ba@baab
\d匹配任意1个数字(0-9)正则\\d\\d可匹配1299
\D匹配任意1个非数字正则\\D可匹配a@
\w匹配任意1个单词字符(字母/数字/下划线),可匹配汉字正则\\w可匹配A5_
\W匹配任意1个非单词字符正则\\W可匹配@+空格
\s匹配任意1个空白字符(空格、制表符\t、换行\n正则a\\sb可匹配a ba\tb
\S匹配任意1个非空白字符正则\\S可匹配1a@
\转义字符,用于匹配特殊符号(如.*?正则\\.可匹配.\\*可匹配*

1.3 数量元字符(限定匹配次数)

上述元字符仅能匹配1个字符,数量元字符用于指定匹配次数,写在“要限定次数的字符/元字符”后面,是实现“多字符匹配”的核心。

数量元字符含义示例
*匹配0次或多次(可有可无,贪婪匹配,尽可能多匹配)正则a*b可匹配babaaab
+匹配1次或多次(至少1次)正则a+b可匹配abaaab,不匹配b
?匹配0次或1次(最多1次,可选)正则a?b可匹配bab,不匹配aab
{n}匹配恰好n次(n为非负整数)正则\\d{6}可匹配6位数字验证码(如123456
{n,}匹配至少n次(n次及以上)正则\\d{2,}可匹配2位及以上数字(如121234
{n,m}匹配n到m次(包含n和m)正则\\w{4,16}可匹配4-16位用户名(字母/数字/下划线)

1.4 边界元字符(限定匹配位置)

默认情况下,正则会从字符串中找任意位置的匹配内容,边界元字符可限定“匹配必须在字符串的开头/结尾”,避免出现“部分匹配”的问题(如验证手机号时,避免匹配到11位数字以外的内容)。

边界元字符含义示例
^匹配字符串的开头正则^1,仅匹配以1开头的字符串(如13800138000
$匹配字符串的结尾正则\\d$,仅匹配以数字结尾的字符串(如abc123
\b匹配单词边界(单词与非单词字符的分隔处)正则\\bjava\\b,可匹配java,不匹配javacajava

1.5 分组与捕获(进阶)

使用括号()可将正则表达式分为多个分组,用于提取结构化信息(如从日期字符串中提取年、月、日)。分组编号从1开始,group(0)表示整个匹配内容,group(1)表示第一个分组,以此类推。

示例:正则(\\d{4})-(\\d{2})-(\\d{2}),可匹配日期格式2026-04-11,其中:

  • group(0):整个匹配内容(2026-04-11

  • group(1):第一个分组(年,2026

  • group(2):第二个分组(月,04

  • group(3):第三个分组(日,11

二、Java正则核心类:Pattern与Matcher

Java中没有内置的正则语法,而是通过java.util.regex包提供的两个核心类实现正则操作——Pattern(正则表达式的编译表示)和Matcher(执行匹配操作的引擎),二者配合使用,是Java正则的标准用法。

2.1 Pattern类:编译正则表达式

Pattern类表示一个已编译的正则表达式,它不能通过构造函数实例化,只能通过静态方法compile(String regex)创建实例。编译过程会校验正则语法的合法性,若语法错误,会抛出PatternSyntaxException

核心特点:Pattern实例是不可变的,线程安全,可被多个线程共享;编译正则表达式有一定开销,因此频繁使用的正则,建议复用Pattern实例,避免重复编译。

常用方法:

  • Pattern compile(String regex):将字符串形式的正则表达式编译为Pattern对象。

  • Matcher matcher(CharSequence input):创建Matcher实例,用于对输入字符串执行匹配操作。

  • boolean matches(String regex, CharSequence input):静态便捷方法,一次性匹配(内部会编译正则,频繁使用效率低)。

  • String pattern():返回原始的正则表达式字符串。

2.2 Matcher类:执行匹配操作

Matcher类是状态ful的匹配引擎,通过Pattern的matcher(CharSequence input)方法创建,专门用于对输入字符串执行匹配、查找、提取、替换等操作。Matcher实例不是线程安全的,每次使用应单独创建。

常用方法(高频):

  • boolean matches():判断整个输入字符串是否完全匹配正则表达式(等价于用^......$包裹正则)。

  • boolean find():从输入字符串中查找下一个匹配的子串,成功返回true,可多次调用(自动从上次结束位置继续查找)。

  • String group():返回当前匹配到的子串(需在find()或matches()返回true后调用,默认返回group(0))。

  • String group(int index):返回指定分组的内容(index从1开始)。

  • int start():返回当前匹配子串的起始位置(下标从0开始)。

  • int end():返回当前匹配子串的结束位置(不包含结束下标)。

  • String replaceAll(String replacement):将所有匹配到的子串替换为指定字符串,支持用$1$2引用分组。

2.3 标准使用流程(推荐)

复用Pattern实例,配合Matcher执行匹配操作,适合频繁使用的场景,效率更高,是工程实践中的推荐写法:

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

public class RegexDemo {
    // 复用Pattern实例,避免重复编译(推荐声明为静态常量)
    private static final Pattern DATE_PATTERN = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})");

    public static void main(String[] args) {
        String input = "今天是2026-04-11,明天是2026-04-12";
        
        // 1. 创建Matcher实例
        Matcher matcher = DATE_PATTERN.matcher(input);
        
        // 2. 查找所有匹配的子串(日期)
        while (matcher.find()) {
            String fullDate = matcher.group(0); // 整个匹配内容
            String year = matcher.group(1);     // 年(第一个分组)
            String month = matcher.group(2);    // 月(第二个分组)
            String day = matcher.group(3);      // 日(第三个分组)
            System.out.printf("完整日期:%s,年:%s,月:%s,日:%s%n", fullDate, year, month, day);
        }
    }
}

输出结果:

2.4 便捷写法(适合一次性匹配)

若仅需执行一次匹配操作,可使用Pattern的静态方法matches(),或String类的matches()方法(底层也是调用Pattern),写法更简洁,但效率较低(每次都会重新编译正则)。

// 方式1:Pattern静态方法
boolean isNumber = Pattern.matches("\\d+", "12345"); // true

// 方式2:String类方法(底层调用Pattern.matches())
boolean isPhone = "13800138000".matches("^1[3-9]\\d{9}$"); // true

三、Java正则实战场景(开发高频)

结合实际开发场景,整理以下高频正则用法,直接复制可用,同时覆盖表单验证、日志提取、字符串清洗等核心需求。

3.1 表单验证(最常用)

表单验证是正则的核心应用场景,如手机号、邮箱、身份证、用户名等格式校验,以下是常用正则及实现代码:

import java.util.regex.Pattern;

/**
 * 表单验证工具类(常用正则封装)
 */
public class RegexValidator {
    // 手机号(中国,三大运营商,11位)
    public static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
    // 邮箱(常见格式,支持字母、数字、下划线、点号、连字符)
    public static final Pattern EMAIL_PATTERN = Pattern.compile("^(a-zA-Z0-9_.+-)+@(a-zA-Z0-9-)+\\.(a-zA-Z0-9-.)+$");
    // 身份证(18位,最后一位可为X/x)
    public static final Pattern ID_CARD_PATTERN = Pattern.compile("^\\d{17}(\\d|X|x)$");
    // 用户名(4-16位,字母、数字、下划线、连字符)
    public static final Pattern USERNAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_-]{4,16}$");
    // 密码(6-20位,包含字母和数字,可选特殊符号)
    public static final Pattern PASSWORD_PATTERN = Pattern.compile("^(?=.*[a-zA-Z])(?=.*\\d).{6,20}$");

    /**
     * 验证字符串是否符合指定正则
     * @param pattern 正则Pattern
     * @param input 待验证字符串
     * @return 符合返回true,否则false
     */
    public static boolean validate(Pattern pattern, String input) {
        if (input == null || input.isEmpty()) {
            return false;
        }
        return pattern.matcher(input).matches();
    }

    // 测试
    public static void main(String[] args) {
        System.out.println(validate(PHONE_PATTERN, "13800138000")); // true
        System.out.println(validate(EMAIL_PATTERN, "test@163.com")); // true
        System.out.println(validate(ID_CARD_PATTERN, "110101199001011234")); // true
        System.out.println(validate(USERNAME_PATTERN, "user_123")); // true
        System.out.println(validate(PASSWORD_PATTERN, "abc123")); // true
    }
}

3.2 日志提取(实战重点)

开发中经常需要从日志中提取关键信息(如时间戳、日志级别、请求ID),正则是最高效的方式。示例:从日志中提取时间戳和日志级别。

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

public class LogExtractDemo {
    public static void main(String[] args) {
        // 日志内容
        String log = "2026-04-11 10:30:00 INFO [main] com.example.Demo - 程序启动成功\n" +
                     "2026-04-11 10:30:05 ERROR [main] com.example.Demo - 数据库连接失败\n" +
                     "2026-04-11 10:30:10 WARN [main] com.example.Demo - 配置文件未找到";

        // 正则:匹配时间戳(yyyy-MM-dd HH:mm:ss)和日志级别(INFO/ERROR/WARN)
        Pattern logPattern = Pattern.compile("(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}) (INFO|ERROR|WARN)");
        Matcher matcher = logPattern.matcher(log);

        // 提取所有匹配的信息
        while (matcher.find()) {
            String time = matcher.group(1);
            String level = matcher.group(2);
            System.out.printf("时间:%s,日志级别:%s%n", time, level);
        }
    }
}

输出结果:

3.3 字符串清洗与替换

正则可快速实现字符串清洗(如去除空白字符、特殊字符、HTML标签)和指定内容替换(如敏感词替换)。

import java.util.regex.Pattern;

public class StringCleanDemo {
    public static void main(String[] args) {
        String input = "  这是一段带有 空白字符、特殊字符!@# 和HTML标签<div>的文本  \n";

        // 1. 去除所有空白字符(空格、制表符、换行)
        String noBlank = input.replaceAll("\\s+", "");
        System.out.println("去除空白后:" + noBlank); // 这是一段带有空白字符、特殊字符!@#和HTML标签<div>的文本

        // 2. 去除特殊字符(保留中英文、数字)
        String noSpecial = noBlank.replaceAll("[^a-zA-Z0-9\u4e00-\u9fa5]", "");
        System.out.println("去除特殊字符后:" + noSpecial); // 这是一段带有空白字符特殊字符和HTML标签div的文本

        // 3. 替换敏感词(将"空白字符"替换为"***")
        String noSensitive = noSpecial.replaceAll("空白字符", "***");
        System.out.println("替换敏感词后:" + noSensitive); // 这是一段带有***特殊字符和HTML标签div的文本
    }
}

3.4 字符串分割

使用正则分割字符串,可解决“多分隔符分割”“不定长分隔符分割”的问题(String的split()方法支持正则)。

public class StringSplitDemo {
    public static void main(String[] args) {
        // 多分隔符分割(逗号、分号、空格)
        String str1 = "java,python;c++ javaScript";
        String[] arr1 = str1.split("[,;\\s]+");
        for (String s : arr1) {
            System.out.print(s + " "); // java python c++ javaScript
        }
        System.out.println();

        // 不定长分隔符分割(多个连字符)
        String str2 = "java---python--c++-javascript";
        String[] arr2 = str2.split("-+");
        for (String s : arr2) {
            System.out.print(s + " "); // java python c++ javascript
        }
    }
}

四、Java正则避坑指南(面试+开发重点)

正则看似简单,但容易出现语法错误、匹配异常、性能问题,以下是高频坑点及解决方案,也是面试中常考的知识点。

4.1 转义字符坑(最易踩坑)

Java中,正则字符串中的\需要转义为\\,否则会被Java字符串解析为转义字符,导致正则失效。

补充:若需匹配\本身,正则需写为\\\\(Java字符串解析后为\\,正则引擎解析后为\)。

4.2 贪婪匹配与非贪婪匹配坑

默认情况下,数量元字符(*+?)是贪婪匹配,会尽可能多匹配内容,可能导致匹配结果不符合预期。在数量元字符后加?,可改为非贪婪匹配(尽可能少匹配)。

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

public class GreedyDemo {
    public static void main(String[] args) {
        String input = "<div>Java</div><div>Python</div>";

        // 贪婪匹配(默认):.*会尽可能多匹配,导致匹配整个字符串
        Pattern greedyPattern = Pattern.compile("<div>.*</div>");
        Matcher greedyMatcher = greedyPattern.matcher(input);
        while (greedyMatcher.find()) {
            System.out.println("贪婪匹配:" + greedyMatcher.group()); // <div>Java</div><div>Python</div>
        }

        // 非贪婪匹配:.*?尽可能少匹配,匹配到第一个</div>就停止
        Pattern nonGreedyPattern = Pattern.compile("<div>.*?</div>");
        Matcher nonGreedyMatcher = nonGreedyPattern.matcher(input);
        while (nonGreedyMatcher.find()) {
            System.out.println("非贪婪匹配:" + nonGreedyMatcher.group()); // 分别匹配两个div标签
        }
    }
}

4.3 性能陷阱:灾难性回溯

Java的正则引擎是回溯引擎,当正则表达式存在嵌套量词(如(a+)+)或模糊匹配时,遇到不匹配的字符串,会进行大量回溯,导致CPU占用飙升、程序卡顿,甚至崩溃。

避坑方案:

  • 避免使用嵌套量词(如(a+)+(.*)*);

  • 使用占有量词(++*+?+)替代贪婪量词,避免回溯;

  • 复杂正则尽量拆分,降低匹配复杂度。

4.4 边界匹配坑

验证格式时,忘记使用^$,会导致“部分匹配”,比如验证手机号时,匹配到包含11位数字的字符串就返回true,而非严格的11位数字。

4.5 线程安全坑

Pattern实例是线程安全的,可被多个线程共享;但Matcher实例不是线程安全的,不能在多个线程中共享同一个Matcher对象,否则会导致匹配结果异常。

正确做法:每个线程单独创建Matcher实例,或使用局部变量存储Matcher。

五、总结

开发中,建议将常用正则封装为工具类,复用Pattern实例,避免重复编译;同时注意避坑,尤其是转义字符、贪婪匹配、性能回溯等问题。面试中,正则的语法、核心类用法、避坑点是高频考点,结合本文的实战示例,可轻松应对。 Java正则表达式详解:从入门到实战,搞定文本处理难题 在Java开发中,文本处理是高频场景——表单验证、日志提取、字符串清洗、数据解析等,都离不开正则表达