杰瑞科技汇

Java String正则表达式怎么用?

核心概念

在 Java 中,与正则表达式相关的核心类有三个:

  1. Pattern: 正则表达式的编译表示,它是一个不可变的类,用于定义一个模式,你可以把它看作是“规则”本身。
  2. **Matcher``: 对输入字符串进行解释和匹配操作的引擎,它是一个状态机器,用于执行Pattern` 定义的规则,你可以把它看作是“执行规则的裁判”。
  3. String: Java 字符串类本身也提供了一些便捷方法(如 matches(), replaceAll() 等),它们内部会自动为你创建 PatternMatcher 对象。

基本使用流程

最标准、最灵活的使用流程分为三步:

编译正则表达式:Pattern.compile()

你需要将你的正则表达式字符串编译成一个 Pattern 对象,这样做的好处是,如果你需要对同一个正则表达式进行多次匹配,编译只需进行一次,可以提高效率。

import java.util.regex.Pattern;
// 定义一个正则表达式,用于匹配一个或多个数字
String regex = "\\d+"; 
// 注意:在 Java 字符串中,反斜杠 \ 是一个转义字符,所以要匹配 \d 需要写成 \\d
// 将正则表达式编译成一个 Pattern 对象
Pattern pattern = Pattern.compile(regex);

创建匹配器:pattern.matcher()

有了 Pattern 对象后,你可以用它来创建一个 Matcher 对象,并将你想要匹配的 String 传入。

String input = "我的电话是 123456,邮政编码是 100086";
// 创建一个 Matcher 对象,将输入字符串与模式进行关联
Matcher matcher = pattern.matcher(input);

执行匹配操作

Matcher 对象提供了丰富的方法来执行匹配操作:

方法 描述 示例 (输入: "abc123 is good")
matches() 整个字符串是否完全匹配模式。 pattern.matches(".*\\d+.*") 返回 true
lookingAt() 字符串开头部分是否匹配模式。 matcher.lookingAt() 返回 true (匹配 "abc123")
find() 在字符串中查找下一个匹配子串。 matcher.find() 第一次返回 true (找到 "123")
start() 返回当前匹配子串的起始索引 matcher.start() 返回 3
end() 返回当前匹配子串的结束索引 + 1。 matcher.end() 返回 6
group() 返回当前匹配的子串 matcher.group() 返回 "123"

完整代码示例

下面是一个综合示例,演示了上述所有核心操作。

import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexDemo {
    public static void main(String[] args) {
        // 1. 定义正则表达式并编译
        // 匹配一个或多个数字
        String regex = "\\d+";
        Pattern pattern = Pattern.compile(regex);
        // 2. 准备要匹配的字符串
        String input = "订单号: 20251027001, 金额: 599.99, 用户ID: 8888";
        // 3. 创建 Matcher
        Matcher matcher = pattern.matcher(input);
        System.out.println("原始字符串: \"" + input + "\"");
        System.out.println("正则表达式: \"" + regex + "\"");
        System.out.println("---------------------------------");
        // 示例 1: find() - 查找所有匹配项
        System.out.println("--- 使用 find() 查找所有数字 ---");
        while (matcher.find()) {
            // group(): 获取匹配到的字符串
            // start(): 获取匹配的起始位置
            // end(): 获取匹配的结束位置 (end - 1 是最后一个字符的索引)
            System.out.println("找到匹配: '" + matcher.group() + 
                               "', 位置: " + matcher.start() + " - " + (matcher.end() - 1));
        }
        System.out.println("---------------------------------");
        // 重置 Matcher,以便重新匹配
        matcher.reset();
        // 示例 2: lookingAt() - 从开头匹配
        System.out.println("--- 使用 lookingAt() 从开头匹配 ---");
        if (matcher.lookingAt()) {
            System.out.println("开头匹配成功: '" + matcher.group() + "'");
        } else {
            System.out.println("开头匹配失败");
        }
        System.out.println("---------------------------------");
        // 重置 Matcher
        matcher.reset();
        // 示例 3: matches() - 整个字符串匹配
        System.out.println("--- 使用 matches() 整个字符串匹配 ---");
        // 修改输入字符串来测试 matches
        String fullMatchInput = "20251027001";
        Matcher fullMatcher = pattern.matcher(fullMatchInput);
        if (fullMatcher.matches()) {
            System.out.println("整个字符串匹配成功: '" + fullMatcher.group() + "'");
        } else {
            System.out.println("整个字符串匹配失败");
        }
    }
}

输出结果:

原始字符串: "订单号: 20251027001, 金额: 599.99, 用户ID: 8888"
正则表达式: "\d+"
---------------------------------
--- 使用 find() 查找所有数字 ---
找到匹配: '20251027001', 位置: 4 - 13
找到匹配: '599', 位置: 20 - 22
找到匹配: '8888', 位置: 31 - 34
---------------------------------
--- 使用 lookingAt() 从开头匹配 ---
开头匹配成功: '20251027001'
---------------------------------
--- 使用 matches() 整个字符串匹配 ---
整个字符串匹配成功: '20251027001'

String 类中的便捷方法

对于一些简单的匹配、替换或分割操作,你不需要手动创建 PatternMatcherString 类本身提供了以下便捷方法:

boolean matches(String regex)

检查整个字符串是否匹配给定的正则表达式。

String str = "123abc";
// 检查 str 是否以数字开头,后跟任意字母
boolean isMatch = str.matches("\\d+.*"); // 返回 true

String replaceAll(String regex, String replacement)

替换所有匹配正则表达式的子串。

String str = "hello 123 world 456";
// 将所有数字替换为 "NUM"
String result = str.replaceAll("\\d+", "NUM"); // 结果: "hello NUM world NUM"

String replaceFirst(String regex, String replacement)

替换第一个匹配正则表达式的子串。

String str = "hello 123 world 456";
// 将第一个数字替换为 "NUM"
String result = str.replaceFirst("\\d+", "NUM"); // 结果: "hello NUM world 456"

String[] split(String regex)

根据正则表达式的匹配来拆分字符串。

String str = "apple,banana,orange,grape";
// 使用逗号或分号作为分隔符
String[] fruits = str.split("[,;]"); // 结果: ["apple", "banana", "orange", "grape"]

常用正则表达式元字符

掌握正则表达式,关键在于理解元字符的含义。

类别 元字符 描述 示例
字符类 匹配除换行符以外的任意单个字符 a.c 可以匹配 "abc", "aac", "a!c"
[...] 匹配方括号中的任意一个字符 [abc] 可以匹配 "a", "b", 或 "c"
[^...] 匹配不在方括号中的任意一个字符。 [^abc] 可以匹配 "d", "1", "@" 等
[a-z] 匹配 a 到 z 范围内的任意小写字母。 [a-z] 可以匹配 "a" 到 "z"
[A-Z] 匹配 A 到 Z 范围内的任意大写字母。 [A-Z] 可以匹配 "A" 到 "Z"
[0-9] 匹配 0 到 9 范围内的任意数字。 等同于 \d
\d 匹配数字,等同于 [0-9] \d{3} 可以匹配 "123", "456"
\D 匹配非数字字符。 \D 可以匹配 "a", " ", "@"
\w 匹配单词字符 (字母, 数字, 下划线),等同于 [a-zA-Z0-9_] \w+ 可以匹配 "hello", "user_123"
\W 匹配非单词字符 \W 可以匹配 " ", "@", "#"
\s 匹配空白字符 (空格, 制表符 \t, 换行符 \n 等)。 \s+ 可以匹配 " ", "\t\n"
\S 匹配非空白字符 \S 可以匹配 "a", "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个数字
定位符 ^ 匹配字符串的开始位置。 ^hello 匹配 "hello world" 的开头
匹配字符串的结束位置。 world$ 匹配 "hello world" 的结尾
\b 匹配单词边界 (单词的开头或结尾)。 \bword\b 可以匹配 "word",但不能匹配 "sword"
\B 匹配非单词边界 \Bword\B 可以匹配 "swordword" 中的 "word"
逻辑操作 操作,匹配 两边的表达式。 cat|dog 可以匹配 "cat" 或 "dog"
分组,将括号内的表达式作为一个整体。 (ab)+ 可以匹配 "ab", "abab", "ababab"
非捕获分组,分组但不保存匹配结果,效率更高。 (?:ab)+ 同上,但不创建捕获组

高级特性:捕获组

捕获组允许你从匹配的字符串中提取出特定的部分,用圆括号 来定义一个组。

String input = "姓名: 张三, 年龄: 30";
// 使用括号创建两个捕获组:一个用于姓名,一个用于年龄
String regex = "姓名: (.*), 年龄: (\\d+)";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(input);
if (matcher.find()) {
    // group(0) 是整个匹配的字符串
    System.out.println("完整匹配: " + matcher.group(0)); // 输出: 姓名: 张三, 年龄: 30
    // group(1) 是第一个捕获组
    System.out.println("姓名: " + matcher.group(1)); // 输出: 张三
    // group(2) 是第二个捕获组
    System.out.println("年龄: " + matcher.group(2)); // 输出: 30
}

性能考虑与最佳实践

  1. 预编译模式 (Pattern.compile): 如果你需要对同一个正则表达式进行多次匹配(在一个循环中处理多个字符串),务必先将其编译为 Pattern 对象,重复编译相同的正则表达式会严重影响性能。
  2. 避免贪婪模式: 默认情况下,数量词(如 , , )是“贪婪”的,它们会匹配尽可能多的字符,如果需要匹配尽可能少的字符,可以使用“懒惰”模式,即在数量词后加上 。
    • 贪婪: 在 "<a> b </a>" 中会匹配 "<a> b </a>"
    • 懒惰: 在 "<a> b </a>" 中会先匹配 ,然后是 "<a>",然后是 "<a> b ",最后是 "<a> b </a>"
  3. 使用具体的字符类: 尽量使用具体的字符类(如 \d 代替 [0-9]\w 代替 [a-zA-Z0-9_]),它们通常更高效且可读性更好。
  4. 注意回溯: 复杂的正则表达式,特别是包含嵌套量词(如 (a+)+)的,可能会导致“ catastrophic backtracking”(灾难性回溯),使程序在处理某些特定字符串时变得异常缓慢,在设计复杂模式时要警惕这种情况。

希望这份详细的指南能帮助你掌握 Java 中使用正则表达式处理 String 的技巧!

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