regex Java中的正则表达式

js4nwp54  于 2023-10-22  发布在  Java
关注(0)|答案(2)|浏览(101)

我有一个函数,它接受一个正则表达式和另一个字符串,并返回一个lambda,该lambda将正则表达式与任何输入相匹配。我希望另一个字符串能够使用正则表达式中的匹配组,但在Java中找不到这样做的方法。
最小示例:

public static Function<String, AbstractMap.SimpleEntry> process(String regex, String template){
  return input -> {
    Matcher m = Pattern.compile(regex).matcher(input);
    if(!m.find()){
      return null;
    }
    // Want something like:
    // String key = m.expand(template);
    // That *only* expands template and doesn't add anything else.
        
    // **Doesn't work**
    // m.replaceFirst/appendReplacement keep parts of the original input
    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> func1 = process("myaddress.com.*[?&]q=(\\w+)", "$1");
  Function<String, AbstractMap.SimpleEntry> func2 = process("myaddress.com.*[?&]q=(?<query>\\w+)", "query: ${query}");
  System.out.println(func1.apply(text).getKey());
  // Outputs "https://www.taco&temp=1" want "taco"
  System.out.println(func2.apply(text).getKey());
  // Outputs "https://www.query: taco&temp=1" want "query: taco"
}

这个例子只使用了一个捕获组,但是regex/template可以是任何东西,我们应该一般性地支持它(例如:进程应该将$1 $4 ${mygroup}作为template处理,以兼容regex)。强制用户匹配整个URL也是不可取的。
Golang为此提供了一个Expand函数,如何在Java中实现它而不重新实现$捕获组语法的解析?
我目前最好的解决方法是在编译正则表达式字符串时,将.*前置并追加到正则表达式字符串。

bgtovc5b

bgtovc5b1#

  • "....."*

返回捕获值的唯一方法是通过 PatternMatcher 类。
或者,如您所提到的,使用附加的.*

  • ".我希望另一个字符串能够使用正则表达式中的匹配组,但我在Java中找不到这样做的方法。... *
// Outputs "https://www.taco&temp=1" want "taco"

实现模板语法并不困难。
Matcher#namedGroups 方法返回捕获组名称及其组编号的 Map

String string = template;
for (int i = 1; i <= m.groupCount(); i++)
    string = string.replaceAll("\\$" + i, m.group(i));
for (Map.Entry<String, Integer> e : m.namedGroups().entrySet())
    string = string.replaceAll("\\$\\{" + e.getKey() + "}", m.group(e.getValue()));

这里是完整的重构因子。

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;
        String string = template;
        for (int i = 1; i <= m.groupCount(); i++)
            string = string.replaceAll("\\$" + i, m.group(i));
        for (Map.Entry<String, Integer> e : m.namedGroups().entrySet())
            string = string.replaceAll("\\$\\{" + e.getKey() + "}", m.group(e.getValue()));
        return new AbstractMap.SimpleEntry<>(string, 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}");
    System.out.println(func1.apply(text).getKey());
    // Outputs "https://www.taco&temp=1" want "taco"
    System.out.println(func2.apply(text).getKey());
    // Outputs "https://www.query: taco&temp=1" want "query: taco"
}

输出

taco
query: taco
c7rzv4ha

c7rzv4ha2#

用我的变通方法来解决我的问题,因为它似乎是最优雅的解决方案。
Java中目前还没有这样的方法;最好的解决方法是将匹配扩展到整个字符串。
根据您使用的Java's three match operations的不同,扩展匹配看起来有点不同:

  • matches-这已经与完整字符串匹配;匹配将必然包括所有输入。
  • lookingAt-从输入开始匹配,将.*附加到正则表达式中,以在match 1中包含完整的输入。
  • find-匹配输入中的任何子字符串,将 * 和 * 前缀.*添加到正则表达式以匹配完整输入。

然后,您可以将原始解决方案与replaceFirst一起使用,它将按预期工作。
请注意,由于您将匹配扩展到整个输入,因此一旦这样做,任何匹配操作都等效于使用matches
1等价地,在这种情况下,您可以使用appendReplacement而不调用appendTail,而不是扩展匹配。

相关问题