You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中如何实现类似Go Expand的正则捕获组模板扩展?

Java实现捕获组模板纯展开功能

我需要实现一个函数,接收正则表达式和模板字符串,返回Lambda表达式用于匹配任意输入,且模板能直接使用正则的捕获组(支持$1、${命名组}等语法),但Java中没有类似Golang regexp.ExpandString 的现成方法。当前用replaceFirst会保留原输入未匹配部分,不符合需求,也不想强制用户修改正则去匹配整个输入。

问题示例代码

import java.util.AbstractMap;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexExpandExample {
    public static Function<String, AbstractMap.SimpleEntry<String, String>> process(String regex, String template) {
        return input -> {
            Matcher m = Pattern.compile(regex).matcher(input);
            if (!m.find()) {
                return null;
            }
            // 此处用replaceFirst会保留原输入未匹配部分,不符合预期
            String key = m.replaceFirst(template);
            return Map.entry(key, input);
        };
    }

    public static void main(String[] args) throws Exception {
        String text = "https://www.myaddress.com?x=y&w=z&other=other&q=taco&temp=1";
        Function<String, AbstractMap.SimpleEntry<String, String>> func1 = process("myaddress.com.*[?&]q=(\\w+)", "$1");
        Function<String, AbstractMap.SimpleEntry<String, String>> func2 = process("myaddress.com.*[?&]q=(?<query>\\w+)", "query: ${query}");
        // 实际输出:https://www.taco&temp=1,期望输出:taco
        System.out.println(func1.apply(text).getKey());
        // 实际输出:https://www.query: taco&temp=1,期望输出:query: taco
        System.out.println(func2.apply(text).getKey());
    }
}

解决方案

核心思路是:找到匹配内容后,仅针对整个匹配的子串进行模板替换,这样就能得到纯捕获组展开后的结果,不会携带原输入的其他内容。同时提前编译正则表达式,提升性能。

修改后的代码:

import java.util.AbstractMap;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexExpandFixed {
    public static Function<String, AbstractMap.SimpleEntry<String, String>> process(String regex, String template) {
        // 提前编译正则,避免每次匹配重复编译
        Pattern pattern = Pattern.compile(regex);
        return input -> {
            Matcher m = pattern.matcher(input);
            if (!m.find()) {
                return null;
            }
            // 提取正则匹配到的完整子串
            String matchedContent = m.group();
            // 仅对匹配到的子串应用模板替换,得到纯展开结果
            String key = pattern.matcher(matchedContent).replaceFirst(template);
            return new AbstractMap.SimpleEntry<>(key, input);
        };
    }

    public static void main(String[] args) throws Exception {
        String text = "https://www.myaddress.com?x=y&w=z&other=other&q=taco&temp=1";
        Function<String, AbstractMap.SimpleEntry<String, String>> func1 = process("myaddress.com.*[?&]q=(\\w+)", "$1");
        Function<String, AbstractMap.SimpleEntry<String, String>> func2 = process("myaddress.com.*[?&]q=(?<query>\\w+)", "query: ${query}");
        // 输出:taco
        System.out.println(func1.apply(text).getKey());
        // 输出:query: taco
        System.out.println(func2.apply(text).getKey());
    }
}

原理说明

  1. 提前编译Pattern:避免每次调用Lambda时重复编译正则,提升执行效率。
  2. 提取匹配子串:通过m.group()获取正则匹配到的完整内容,这部分包含所有捕获组的信息。
  3. 针对匹配子串替换:用原正则匹配这个子串,调用replaceFirst(template)时,会将整个匹配子串替换为模板展开后的内容,最终得到的就是纯捕获组替换后的模板字符串,不会携带原输入的其他部分。

这种方法完全复用Java正则的原生替换语法,不需要自己解析$占位符,支持数字捕获组($1、$2)和命名捕获组(${groupName}),满足通用场景需求。

内容的提问来源于stack exchange,提问作者River

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 06:56:04