杰瑞科技汇

Java字符串匹配如何用正则表达式实现?

正则表达式(Regular Expression,简称 Regex)是一种强大的文本处理工具,它使用一种特定的语法来描述和匹配字符串模式,在 Java 中,java.util.regex 包提供了对正则表达式的支持。

Java字符串匹配如何用正则表达式实现?-图1
(图片来源网络,侵删)

核心组件

Java 正则表达式主要由两个类组成:

  1. java.util.regex.Pattern: 一个编译后的正则表达式模式,它是一个不可变的类,代表了一个经过编译的模式,你可以把它看作是“规则”本身。
  2. java.util.regex.Matcher: 对输入字符串进行解释和匹配的引擎,它是一个状态机器,用于执行 Pattern 定义的各种操作(如匹配、查找、替换等)。

使用正则表达式的基本流程是:

  1. 编译一个正则表达式字符串,得到一个 Pattern 对象。
  2. 使用 Pattern 对象和你的输入字符串创建一个 Matcher 对象。
  3. 通过 Matcher 对象执行匹配操作并获取结果。

常用方法示例

下面我们通过一系列示例来学习最常用的方法。

matches() - 完全匹配

matches() 方法尝试将整个输入字符串与模式进行匹配,如果整个字符串完全匹配模式,则返回 true

Java字符串匹配如何用正则表达式实现?-图2
(图片来源网络,侵删)

示例: 验证一个字符串是否是一个有效的手机号(假设规则是 11 位数字)。

import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class RegexMatchesExample {
    public static void main(String[] args) {
        String regex = "^\\d{11}$"; // 正则表达式:^表示开始,$表示结束,\d表示数字,{11}表示11次
        String phone1 = "13812345678";
        String phone2 = "123456789012"; // 12位,不匹配
        String phone3 = "138-1234-5678"; // 包含'-',不匹配
        // 创建 Pattern 对象 (建议使用静态的 compile 方法)
        Pattern pattern = Pattern.compile(regex);
        // 测试 phone1
        Matcher matcher1 = pattern.matcher(phone1);
        System.out.println(phone1 + " 是否匹配: " + matcher1.matches()); // true
        // 测试 phone2
        Matcher matcher2 = pattern.matcher(phone2);
        System.out.println(phone2 + " 是否匹配: " + matcher2.matches()); // false
        // 测试 phone3
        Matcher matcher3 = pattern.matcher(phone3);
        System.out.println(phone3 + " 是否匹配: " + matcher3.matches()); // false
    }
}

输出:

13812345678 是否匹配: true
123456789012 是否匹配: false
138-1234-5678 是否匹配: false

注意: ^ 和 在这里非常重要,如果没有它们,"abc12345678def" 也会被 ^\d{11}$ 匹配为 false,但如果只有 \d{11},它可能会找到子串 "12345678" 而导致 find() 返回 truematches() 总是检查整个字符串。


find()group() - 查找子串

find() 方法在输入字符串中查找与模式匹配的子串,如果找到,返回 trueMatcher 的状态会定位到该匹配项,你可以重复调用 find() 来查找所有匹配项。

找到匹配项后,可以使用 group() 方法获取匹配到的内容。

示例: 从一段文本中提取所有电子邮件地址。

import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class RegexFindExample {
    public static void main(String[] args) {
        String text = "我的邮箱是 zhangsan@example.com,联系我,或者也可以联系 lisi@my-domain.co.uk。";
        String regex = "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b";
        // 在 Java 中,正则表达式默认不区分大小写,但最好明确指定
        Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(text);
        System.out.println("找到的邮箱地址:");
        while (matcher.find()) {
            // group(0) 表示整个匹配的子串
            System.out.println(matcher.group(0));
        }
    }
}

输出:

找到的邮箱地址:
zhangsan@example.com
lisi@my-domain.co.uk

解释:

  • \b: 单词边界,确保我们匹配的是完整的单词,而不是 myaddress@example.com 中的部分。
  • [A-Za-z0-9._%+-]+: 匹配用户名部分,允许字母、数字、点、下划线、百分号、加号和减号。
  • 匹配 "@" 符号。
  • [A-Za-z0-9.-]+: 匹配域名部分。
  • \.: 匹配点号( 在正则中有特殊含义,所以需要转义)。
  • [A-Z|a-z]{2,}: 匹配顶级域名,如 .com, .uk, .co 等,至少2个字母。
  • Pattern.CASE_INSENSITIVE: 编译模式时指定不区分大小写。

lookingAt() - 从开头匹配

lookingAt() 方法尝试从输入字符串的开头开始匹配模式,它不像 matches() 那样要求整个字符串都匹配,但必须是字符串的起始部分匹配即可。

示例:

import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class RegexLookingAtExample {
    public static void main(String[] args) {
        String text = "Hello world, this is a test.";
        String regex = "Hello.*"; // 匹配以 "Hello" 开头,后面跟任意字符(除换行符)零次或多次
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(text);
        if (matcher.lookingAt()) {
            System.out.println("字符串以 'Hello' 开头!");
            System.out.println("匹配到的内容: " + matcher.group());
        } else {
            System.out.println("字符串不以 'Hello' 开头。");
        }
    }
}

输出:

字符串以 'Hello' 开头! Hello world, this is a test.

常用正则表达式元字符

掌握这些元字符是编写正则表达式的基础。

元字符 描述 示例
匹配除换行符以外的任意单个字符 a.c 可以匹配 abc, aac, a!c
^ 匹配字符串的开始 ^abc 匹配 abc123,但不匹配 123abc
匹配字符串的结束 abc$ 匹配 123abc,但不匹配 abc123
\d 匹配一个数字字符,等同于 [0-9] \d{3} 匹配 123, 456
\D 匹配一个非数字字符,等同于 [^0-9] \D+ 匹配 hello,
\w 匹配单词字符(字母、数字、下划线),等同于 [A-Za-z0-9_] \w+ 匹配 Java, user_123
\W 匹配非单词字符,等同于 [^A-Za-z0-9_] \W 匹配 , , ` `
\s 匹配任何空白字符(空格、制表符、换行符等) \s+ 匹配 , "\t"
\S 匹配任何非空白字符 \S+ 匹配 Java, 123
[...] 字符类,匹配方括号内的任意一个字符 [abc] 匹配 a, b, 或 c
[^...] 否定字符类,匹配不在方括号内的任意字符 [^abc] 匹配 d, 1,
匹配前面的元素零次或多次 a* 匹配 (空串), a, aaa
匹配前面的元素一次或多次 a+ 匹配 a, aaa,但不匹配空串
匹配前面的元素零次或一次 a? 匹配 或 a
{n} 精确匹配前面的元素 n 次 \d{3} 匹配3个连续的数字
{n,} 匹配前面的元素至少 n 次 \d{2,} 匹配2个或更多连续的数字
{n,m} 匹配前面的元素至少 n 次,最多 m 次 \d{2,4} 匹配2到4个连续的数字
捕获组,将括号内的内容作为一个整体 (ab)+ 匹配 ab, abab
\| 选择(或)操作符 a\|b 匹配 ab

高级用法:分组和替换

分组与 group()

使用括号 可以创建捕获组。group(0) 总是整个匹配的字符串,group(1) 是第一个捕获组,group(2) 是第二个,以此类推。

示例: 提取日期字符串中的年、月、日。

import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class RegexGroupExample {
    public static void main(String[] args) {
        String dateStr = "今天的日期是 2025-10-27。";
        String regex = "(\d{4})-(\d{2})-(\d{2})";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(dateStr);
        if (matcher.find()) {
            System.out.println("完整日期: " + matcher.group(0));
            System.out.println("年份: " + matcher.group(1));
            System.out.println("月份: " + matcher.group(2));
            System.out.println("日期: " + matcher.group(3));
        }
    }
}

输出:

完整日期: 2025-10-27
年份: 2025
月份: 10
日期: 27

替换操作 replaceAll()replaceFirst()

Matcher 对象提供了强大的替换功能。

  • replaceAll(String replacement): 替换所有匹配的子串。
  • replaceFirst(String replacement): 替换第一个匹配的子串。

replacement 字符串中,可以使用 $n 来引用第 n 个捕获组的内容。

示例: 将字符串中的所有日期格式从 YYYY-MM-DD 改为 DD/MM/YYYY

import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class RegexReplaceExample {
    public static void main(String[] args) {
        String text = "项目开始日期: 2025-01-15, 结束日期: 2025-12-31。";
        String regex = "(\\d{4})-(\\d{2})-(\\d{2})";
        // 替换模式:$3/$2/$1 表示用第三个捕获组,第二个,第一个
        String replacement = "$3/$2/$1";
        // 创建 Matcher 并进行替换
        String result = Pattern.compile(regex).matcher(text).replaceAll(replacement);
        System.out.println("原始文本: " + text);
        System.out.println("替换后文本: " + result);
    }
}

输出:

原始文本: 项目开始日期: 2025-01-15, 结束日期: 2025-12-31。
替换后文本: 项目开始日期: 15/01/2025, 结束日期: 31/12/2025。

Java 8+ 中的便捷方法

对于简单的匹配和查找,你不必总是手动创建 PatternMatcher 对象。String 类本身提供了一些便捷的静态方法,它们内部会为你处理这些对象。

  1. String.matches(String regex): 等价于 Pattern.matches(regex, this)
  2. String.replaceAll(String regex, String replacement): 等价于 Pattern.compile(regex).matcher(this).replaceAll(replacement)
  3. String.replaceFirst(String regex, String replacement): 等价于 Pattern.compile(regex).matcher(this).replaceFirst(replacement)
  4. String.split(String regex): 根据正则表达式分割字符串。

示例:

public class StringRegexExample {
    public static void main(String[] args) {
        String str = "apple,banana;orange|grape";
        // 使用 split 分割字符串
        String[] fruits = str.split("[,;|]"); // 正则表达式:匹配逗号、分号或竖线
        System.out.println("分割结果:");
        for (String fruit : fruits) {
            System.out.println(fruit);
        }
        // 使用 matches 验证
        String email = "test@example.com";
        boolean isValidEmail = email.matches("^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$");
        System.out.println("\n邮箱 " + email + " 是否有效: " + isValidEmail);
    }
}

输出:

分割结果:
apple
banana
orange
grape
邮箱 test@example.com 是否有效: true
操作 推荐方法 适用场景
完全匹配 str.matches(regex)pattern.matcher(str).matches() 验证整个字符串是否符合某个格式(如邮箱、手机号)。
查找子串 pattern.matcher(str).find() + matcher.group() 从文本中提取符合特定模式的内容(如所有链接、所有数字)。
str.replaceAll(regex, replacement)matcher.replaceAll(replacement) 修改文本中符合模式的部分。
分割字符串 str.split(regex) 根据复杂的分隔符(如多个符号)将字符串拆分为数组。

正则表达式非常强大,但也可能变得复杂,建议从简单的模式开始,逐步构建,并多使用在线测试工具(如 Regex101)来验证你的表达式。

分享:
扫描分享到社交APP
上一篇
下一篇