在日常编程开发中,字符串的匹配、分割、替换等操作十分常见 —— 比如验证用户输入的邮箱格式、从文本中提取特定信息、按多种规则拆分字符串等。而正则表达式(Regular Expression)作为一种强大的文本处理工具,能高效解决这类问题。本文将从基础概念切入,结合实例解析正则表达式,并完整展示其在 Java 中的实现方案。

一、正则表达式基本概念

1. 定义

正则表达式(简称 Regex)是一种用于描述字符串模式的特殊文本串,它可以:

  • 检查一个字符串是否符合指定模式(如 “是否为合法手机号”);
  • 从字符串中提取符合模式的内容(如 “提取文本中的所有数字”);
  • 替换字符串中符合模式的部分(如 “将所有敏感字符替换为 *”)。

正则表达式广泛应用于数据验证、日志分析、文本爬虫等场景,是开发者必备的工具之一。

2. 核心组成元素

正则表达式的功能由元字符量词字符类等核心元素组合实现,以下是最常用的元素说明:

元素类型 具体符号 / 语法 含义说明
元字符 . 匹配任意单个字符(除换行符\n外),如a.b可匹配acbaab
^ 匹配字符串的开头,如^Hello仅匹配以 “Hello” 开头的字符串
$ 匹配字符串的结尾,如World$仅匹配以 “World” 结尾的字符串
| 逻辑 “或”,匹配左右任意一个模式,如cat|dog可匹配 “cat” 或 “dog”
\ 转义字符,用于匹配元字符本身(如\.匹配实际的 “.”,\*匹配实际的 “*”)
量词 * 匹配前面的元素零次或多次,如a*可匹配 “”(空串)、“a”、“aa” 等
+ 匹配前面的元素一次或多次,如a+可匹配 “a”、“aa”,但不匹配空串
? 匹配前面的元素零次或一次,如a?可匹配 “” 或 “a”
{n} 匹配前面的元素恰好 n 次,如a{3}仅匹配 “aaa”
{n,} 匹配前面的元素至少 n 次,如a{2,}可匹配 “aa”、“aaa” 等
{n,m} 匹配前面的元素n 到 m 次(含 n 和 m),如a{2,3}可匹配 “aa”、“aaa”
字符类 [abc] 匹配 “a”“b”“c” 中的任意一个字符,等价于a|b|c
[a-z] 匹配任意小写字母(范围匹配),同理[0-9]匹配任意数字,[A-Z]匹配大写字母
[^abc] 匹配 “非 a、非 b、非 c” 的任意字符(^ 在中括号内表示 “非”)
预定义字符类 \d 等价于[0-9],匹配任意数字
\s 匹配任意空白字符(空格、制表符\t、换行符\n等)
\w 等价于[a-zA-Z0-9_],匹配字母、数字或下划线

二、正则表达式常用示例

掌握基础元素后,可组合出满足实际需求的正则表达式,以下是典型场景示例:

1. 数据格式验证

  • 验证邮箱^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+\.[a-zA-Z]{2,}$解析:

    • ^[a-zA-Z0-9_-]+:邮箱前缀(字母、数字、下划线、短横线,至少 1 位);
    • @:固定的 “@” 符号;
    • [a-zA-Z0-9_-]+:邮箱域名(如gmail163);
    • \.[a-zA-Z]{2,}:域名后缀(如.com.cn,至少 2 位字母);
    • $:匹配字符串结尾,避免后缀后多无关字符。
  • 验证手机号(中国大陆)^1[3-9]\d{9}$解析:

    • ^1:以 “1” 开头(手机号固定规则);
    • [3-9]:第二位为 3-9(排除 1、2,对应运营商号段);
    • \d{9}:后续 9 位为数字(总长度 11 位);
    • $:匹配结尾,确保无多余字符。
  • 验证整数或小数^-?\d+(\.\d+)?$解析:

    • ^-?:可选的负号(匹配正数或负数);
    • \d+:至少 1 位整数部分;
    • (\.\d+)?:可选的小数部分(.+ 至少 1 位数字);
    • $:匹配结尾。

2. 字符串分割

  • 按单个分隔符分割:如按空格分割 “one two three”,正则为" "
  • 按多个分隔符分割:如按;,?分割 “abc;123,456?999|haha”,正则为";|,|\\?"?是元字符,需用\转义,Java 中需写\\?);
  • 按任意空白字符分割:正则为"\\s+"(匹配 1 个或多个空白字符,避免多个空格导致空数组元素)。

3. 内容提取与替换

  • 提取文本中的所有数字\d+(匹配 1 个或多个数字);
  • 替换敏感字符:如将字符串中的数字替换为*,正则为"\d"
  • 去除首尾空格^(\s+)|(\s+)$(匹配开头或结尾的 1 个或多个空白字符,替换为空串)。

三、正则表达式的 Java 实现

Java 提供两种使用正则表达式的方式:

  1. String类内置方法:适合简单场景(分割、匹配、替换);
  2. java.util.regexPattern(编译正则)和Matcher(执行匹配),适合复杂场景(多次匹配、提取子串)。

1. String类的正则方法

String类提供split()matches()replaceAll()等便捷方法,无需额外创建类实例。

(1)split(String regex):按正则分割字符串

将字符串按匹配的正则规则拆分为字符串数组,示例如下:

public class RegexSplitDemo {
    public static void main(String[] args) {
        // 待分割字符串(含;、,、?三种分隔符)
        String str = "abc;123,456?999|haha";
        // 正则表达式:匹配;、,或?(?需转义为\\?)
        String regex = ";|,|\\?";
        // 执行分割
        String[] result = str.split(regex);
        
        // 输出结果
        System.out.println("分割后的数组:");
        for (int i = 0; i < result.length; i++) {
            System.out.printf("元素[%d]:%s%n", i, result[i]);
        }
    }
}

输出结果

plaintext

分割后的数组:
元素[0]:abc
元素[1]:123
元素[2]:456
元素[3]:999|haha
(2)matches(String regex):验证字符串是否匹配正则

返回boolean值,true表示完全匹配正则规则(注意:需匹配整个字符串,而非部分),示例如下:

java

运行

public class RegexMatchDemo {
    public static void main(String[] args) {
        // 待验证的邮箱和手机号
        String validEmail = "test_123@example.com";
        String invalidEmail = "test@.com"; // 非法域名
        String validPhone = "13812345678";
        String invalidPhone = "12345678901"; // 第二位为2,非法
        
        // 正则表达式
        String emailRegex = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+\\.[a-zA-Z]{2,}$";
        String phoneRegex = "^1[3-9]\\d{9}$";
        
        // 执行验证
        System.out.printf("邮箱[%s]是否合法:%b%n", validEmail, validEmail.matches(emailRegex));
        System.out.printf("邮箱[%s]是否合法:%b%n", invalidEmail, invalidEmail.matches(emailRegex));
        System.out.printf("手机号[%s]是否合法:%b%n", validPhone, validPhone.matches(phoneRegex));
        System.out.printf("手机号[%s]是否合法:%b%n", invalidPhone, invalidPhone.matches(phoneRegex));
    }
}

输出结果

plaintext

邮箱[test_123@example.com]是否合法:true
邮箱[test@.com]是否合法:false
手机号[13812345678]是否合法:true
手机号[12345678901]是否合法:false
(3)replaceAll(String regex, String replacement):按正则替换

将字符串中所有匹配正则的部分替换为replacement,示例如下:

public class RegexReplaceDemo {
    public static void main(String[] args) {
        // 待处理字符串
        String strWithNum = "Hello123World456"; // 含数字
        String strWithSpace = "  正则表达式 实战  "; // 首尾空格
        
        // 1. 将所有数字替换为*
        String noNumStr = strWithNum.replaceAll("\\d", "*");
        // 2. 去除首尾空格(匹配开头/结尾的空白字符,替换为空串)
        String noSpaceStr = strWithSpace.replaceAll("^(\\s+)|(\\s+)$", "");
        
        // 输出结果
        System.out.printf("原字符串:%s%n替换数字后:%s%n", strWithNum, noNumStr);
        System.out.printf("原字符串:[%s]%n去除空格后:[%s]%n", strWithSpace, noSpaceStr);
    }
}

输出结果

plaintext

原字符串:Hello123World456
替换数字后:Hello***World***
原字符串:[  正则表达式 实战  ]
去除空格后:[正则表达式 实战]

2. PatternMatcher的高级用法

当需要多次匹配(如提取文本中所有符合规则的内容)或获取匹配子串的位置时,需使用PatternMatcher

  • Pattern:编译正则表达式(避免重复编译,提高效率);
  • Matcher:关联待匹配字符串,执行匹配操作。
示例:提取文本中的所有温度值

java

运行

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

public class RegexExtractDemo {
    public static void main(String[] args) {
        // 待提取的文本
        String text = "今天气温25℃,明天预计30℃,后天可能28℃,大后天降温至18℃。";
        // 正则表达式:匹配1个或多个数字(温度值)
        String regex = "\\d+";
        
        // 1. 编译正则表达式(仅需编译一次,可重复使用)
        Pattern pattern = Pattern.compile(regex);
        // 2. 关联待匹配字符串,创建Matcher对象
        Matcher matcher = pattern.matcher(text);
        
        // 3. 提取所有匹配的内容(find()查找下一个匹配项)
        System.out.println("文本中的温度值:");
        int count = 0;
        while (matcher.find()) {
            count++;
            // group():返回当前匹配的子串;start()/end():返回匹配子串的起始/结束索引
            System.out.printf("第%d个温度:%s(位置:%d-%d)%n", 
                              count, matcher.group(), matcher.start(), matcher.end()-1);
        }
    }
}

输出结果

plaintext

文本中的温度值:
第1个温度:25(位置:4-5)
第2个温度:30(位置:12-13)
第3个温度:28(位置:20-21)
第4个温度:18(位置:29-30)

3. 与StringTokenizer的对比

参考文档中提到的StringTokenizer类也可用于分割字符串,但它不支持正则表达式,仅能按固定字符分割,灵活性远低于正则:

  • StringTokenizer:如按 “-” 分割 “2024-10-20”,需指定分隔符"-",无法处理 “2024/10-20”(混合分隔符);
  • 正则split():处理混合分隔符仅需"\\/|-"(匹配/-),无需额外逻辑。

因此,复杂分割场景优先使用正则表达式的split()方法。

四、总结与实践建议

1. 核心优势

正则表达式的核心价值在于用简洁的语法描述复杂的字符串模式,减少冗余的字符串判断代码(如验证邮箱无需逐字符判断,仅需一行正则)。

2. 实践建议

  • 先测试再使用:复杂正则建议用在线工具(如Regex101)验证,避免语法错误;
  • 避免过度复杂:过于冗长的正则(如匹配所有 URL)可拆分为多个步骤,兼顾可读性和维护性;
  • 注意转义字符:Java 中\需写为\\(如\d需写\\d),避免与 Java 的字符串转义冲突;
  • 复用Pattern:多次使用同一正则时,提前编译Pattern(而非每次调用String方法),减少重复编译开销。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐