杰瑞科技汇

Java正则find()如何匹配所有符合条件的子串?

“查找”可以分为几种情况:

Java正则find()如何匹配所有符合条件的子串?-图1
(图片来源网络,侵删)
  1. 查找第一个匹配项:使用 Matcher.find()
  2. 查找所有匹配项:循环调用 Matcher.find()
  3. 查找精确匹配:使用 Matcher.matches()
  4. 查找从开头开始的匹配:使用 Matcher.lookingAt()

下面我们通过代码示例逐一讲解。


核心类简介

在开始之前,先简单了解一下两个核心类:

  • java.util.regex.Pattern:正则表达式的编译表示,它是一个不可变的类,用于定义你要匹配的模式,通常使用静态方法 Pattern.compile(String regex) 来创建一个 Pattern 实例。
  • java.util.regex.Matcher:对输入字符串进行匹配操作的引擎,它提供了各种方法来执行匹配、查找和替换操作,每个 Matcher 实例都与一个特定的 Pattern 和一个特定的输入字符串(CharSequence)关联。

查找第一个匹配项 (Matcher.find())

find() 方法尝试在输入字符串中查找与模式匹配的下一个子序列,如果找到一个匹配,它返回 trueMatcher 的状态会更新为指向该匹配项。

示例:在一段文本中查找第一个单词

Java正则find()如何匹配所有符合条件的子串?-图2
(图片来源网络,侵删)
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class FindFirstExample {
    public static void main(String[] args) {
        String text = "Hello world, this is a regular expression example.";
        // 正则表达式:匹配一个或多个字母
        String regex = "\\w+";
        // 1. 编译正则表达式
        Pattern pattern = Pattern.compile(regex);
        // 2. 创建 Matcher 对象,将模式应用于文本
        Matcher matcher = pattern.matcher(text);
        // 3. 查找第一个匹配项
        if (matcher.find()) {
            // 如果找到,获取匹配的文本
            String firstMatch = matcher.group();
            // 获取匹配的起始和结束索引
            int start = matcher.start();
            int end = matcher.end();
            System.out.println("找到第一个匹配项: \"" + firstMatch + "\"");
            System.out.println("起始位置: " + start);
            System.out.println("结束位置: " + end); // 结束位置是匹配字符之后的位置
        } else {
            System.out.println("未找到匹配项。");
        }
    }
}

输出:

找到第一个匹配项: "Hello"
起始位置: 0
结束位置: 5

查找所有匹配项 (循环调用 find())

这是最常见的需求,只需在一个 while 循环中重复调用 find() 方法,直到它返回 false,即可遍历字符串中所有匹配的子序列。

示例:查找文本中的所有单词

import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class FindAllExample {
    public static void main(String[] args) {
        String text = "One two three, four five 6.";
        // 正则表达式:匹配一个或多个字母
        String regex = "[a-zA-Z]+";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(text);
        System.out.println("所有匹配的单词:");
        while (matcher.find()) {
            // group() 方法返回当前匹配的子字符串
            System.out.println("找到: " + matcher.group() + 
                               " (位置: " + matcher.start() + " - " + (matcher.end() - 1) + ")");
        }
    }
}

输出:

Java正则find()如何匹配所有符合条件的子串?-图3
(图片来源网络,侵删)
所有匹配的单词:
找到: One (位置: 0 - 2)
找到: two (位置: 4 - 6)
找到: three (位置: 8 - 12)
找到: four (位置: 14 - 17)
找到: five (位置: 19 - 22)

精确匹配 (Matcher.matches())

matches() 方法尝试将整个输入字符串与模式进行匹配,只有当整个字符串完全符合模式时,它才返回 true

示例:验证一个字符串是否是有效的手机号码

import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MatchesExample {
    public static void main(String[] args) {
        // 假设手机号码规则是 11 位数字,以 1 开头
        String phoneRegex = "^1\\d{10}$";
        Pattern pattern = Pattern.compile(phoneRegex);
        String phone1 = "13812345678";
        String phone2 = "12345678901"; // 无效号码
        String phone3 = "1381234567";   // 位数不对
        String phone4 = "abc13812345678"; // 包含其他字符
        testPhone(pattern, phone1);
        testPhone(pattern, phone2);
        testPhone(pattern, phone3);
        testPhone(pattern, phone4);
    }
    public static void testPhone(Pattern pattern, String phone) {
        Matcher matcher = pattern.matcher(phone);
        if (matcher.matches()) {
            System.out.println("\"" + phone + "\" 是一个有效的手机号码。");
        } else {
            System.out.println("\"" + phone + "\" 不是一个有效的手机号码。");
        }
    }
}

输出:

"13812345678" 是一个有效的手机号码。
"12345678901" 不是一个有效的手机号码。
"1381234567" 不是一个有效的手机号码。
"abc13812345678" 不是一个有效的手机号码。

从开头开始的匹配 (Matcher.lookingAt())

lookingAt() 方法尝试从输入字符串的开头开始匹配模式,它不像 matches() 那样要求匹配整个字符串,但如果开头不匹配,它就会立即返回 false

示例:检查字符串是否以 "http://" 开头

import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class LookingAtExample {
    public static void main(String[] args) {
        String text1 = "http://www.example.com/path";
        String text2 = "ftp://www.example.com/path";
        String regex = "http://";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher1 = pattern.matcher(text1);
        Matcher matcher2 = pattern.matcher(text2);
        System.out.println("检查文本1: \"" + text1 + "\"");
        if (matcher1.lookingAt()) {
            System.out.println("  -> 开头匹配成功!匹配内容: " + matcher1.group());
        } else {
            System.out.println("  -> 开头匹配失败。");
        }
        System.out.println("\n检查文本2: \"" + text2 + "\"");
        if (matcher2.lookingAt()) {
            System.out.println("  -> 开头匹配成功!匹配内容: " + matcher2.group());
        } else {
            System.out.println("  -> 开头匹配失败。");
        }
    }
}

输出:

检查文本1: "http://www.example.com/path"
  -> 开头匹配成功!匹配内容: http://
检查文本2: "ftp://www.example.com/path"
  -> 开头匹配失败。

总结与对比

方法 描述 匹配范围 返回 true 的条件
find() 查找下一个匹配的子序列。 字符串的任意部分 在字符串中找到任意位置匹配的模式。
matches() 整个输入字符串与模式匹配。 整个字符串 整个输入字符串完全符合模式。
lookingAt() 输入字符串的开头进行匹配。 字符串的开头部分 字符串的开头部分符合模式。

简化的 String 方法

对于简单的查找和替换,String 类本身提供了一些便捷方法,它们内部也使用了 java.util.regex

  • boolean String.matches(String regex): 等价于 Pattern.matches(regex, this)
  • String String.replaceAll(String regex, String replacement): 替换所有匹配项。
  • String String.replaceFirst(String regex, String replacement): 替换第一个匹配项。
  • String[] String.split(String regex): 根据正则表达式分割字符串。

示例:

String text = "apple,banana,orange";
// 使用 String 的便捷方法
String[] fruits = text.split(",");
for (String fruit : fruits) {
    System.out.println(fruit); // 输出 apple, banana, orange
}
boolean isNumeric = "12345".matches("\\d+");
System.out.println("isNumeric: " + isNumeric); // 输出 true

最佳实践

  1. 预编译模式:如果你需要对同一个正则表达式进行多次匹配(在一个循环中处理多个字符串),请务必预先编译 Pattern 对象并重复使用它,这样可以显著提高性能,因为编译正则表达式是一个相对昂贵的操作。
  2. 捕获组:使用括号 在正则表达式中创建捕获组。matcher.group(0) 返回整个匹配的文本,matcher.group(1) 返回第一个捕获组的内容,以此类推。
  3. 处理异常:虽然 Pattern.compile() 可能抛出 PatternSyntaxException,但对于大多数应用程序,这是一个编程时错误,应该在开发阶段就解决,而不是在运行时捕获。

希望这个详细的解释能帮助你完全理解 Java 中正则表达式的“查找”操作!

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