替换捕获组

11
如果我有一个带捕获组的正则表达式,例如foo(_+f)。如果我匹配一个字符串并想要替换所有匹配中的第一个捕获组为baz,那么应该怎么做?
foo___f blah foo________f

转换为:

foobaz blah foobaz

使用标准库似乎没有简单的方法来做到这一点。如果我使用Matcher.replaceAll(),这将替换整个模式的所有匹配并将字符串转换为

baz blah baz

显然,我可以通过迭代匹配结果,存储每个捕获组的开始和结束索引,然后返回并替换它们,但是否有更简单的方法?

谢谢, Don


我很确定我误解了这个问题,因为我立刻想到使用Matcher.replaceFirst而不是replaceAll...!? - Andreas Dolk
4个回答

25

我觉得您想要的是这样的效果?

    System.out.println(
        "foo__f blah foo___f boo___f".replaceAll("(?<=foo)_+f", "baz")
    ); // prints "foobaz blah foobaz boo___f"

在这里,你只需用"baz"替换整个匹配项,但是该匹配项使用了向后查找以确保_+f前面是foo

另请参阅


如果无法使用向后查找(可能是因为长度不是有限的),那么只需捕获即使不进行替换的内容,并在替换字符串中引用它们即可。

    System.out.println(
        "fooooo_f boooo_f xxx_f".replaceAll("(fo+|bo+)(_+f)", "$1baz")
    ); // prints "fooooobaz boooobaz xxx_f"

所以在这里,我们实际上只是替换了\2所匹配到的内容。


1
不错的回答,但是OP似乎已经编辑了匹配模式,删除了g。这对问题的看法产生了很大的变化。我建议您相应地更新您的答案。 - BalusC
2
第二个建议很简单、有效,我应该自己想到,而且不需要我学习lookarounds :) - Dónal
是的,我更新了问题中的模式,试图澄清。如果这混淆了您的回答,我很抱歉。 - Dónal
@Don:向前/向后查找是很棒的,可以看看这个例子:https://dev59.com/G3E85IYBdhLWcg3w430X - polygenelubricants

4

因此,我认为这些答案都不能很好地解决以下问题的更抽象情况,这是我自己遇到的问题,因此我编写了一些代码来处理更一般的情况:

/**
 * 
 * @param regex  Pattern to find in oldLine. Will replace contents in ( ... ) - group(1) - with newValue
 * @param oldLine  Previous String that needs replacing
 * @param newValue  Value that will replace the captured group(1) in regex
 * @return
 */
public static String replace(String regex, String oldLine, String newValue)
{
    Pattern p = Pattern.compile(regex);
    Matcher m = p.matcher(oldLine);
    if (m.find())
    {
        return m.replaceAll(replaceGroup(regex, newValue));
    }
    else
    {
        throw new RuntimeException("No match");
    }
}

/**
 * Replaces group(1) ( ... ) with replacement, and returns the resulting regex with replacement String
 * @param regex  Regular expression whose parenthetical group will be literally replaced by replacement
 * @param replacement  Replacement String
 * @return
 */
public static String replaceGroup(String regex, String replacement)
{
    return regex.replaceAll("\\(.*\\)", replacement);
}

在您的示例中,它确实如您所描述的那样:
String regex = "foo(_+f)";
String line = "foo___f blah foo________f";
System.out.println(FileParsing.replace(regex, line, "baz"));

打印出以下内容:
foobaz blah foobaz

1
p = Pattern.compile("foo(g.*?f)");
m = p.matcher("foog___f blah foog________f");
s = m.replaceAll("foobaz");//replace with foobaz instead of just baz
System.out.println(s);//foobaz blah foobaz

不,我正在尝试替换所有匹配中的捕获组。 - Dónal
1
这就是Amarghosh的代码片段所做的事情。当匹配到"foo"时,它也会被包含在替换字符串中,这意味着任何类似于foo_f、foo____f、foo__f等的实例都会变成foobaz。 - JAB
@Don 为你更新了代码以供测试。正如 @JAB 所提到的,我也在替换字符串中包含了 foo。而你最初发布的正则表达式是贪婪的,而且你的问题不够清晰 - 这就是为什么我问你是否正在寻找懒惰量词的原因。 - Amarghosh

0

这个有没有接近……

String[] s = {"foo___f blah foo________f", 
    "foo___f blah goo________f"};
for(String ss: s)
System.out.println(ss.replaceAll("(foo)(_+)f", "$1baz"));

例如,也为'foo'添加一个捕获组。否则,简单的替换将是:
"foo___f blah foo________f".replaceAll("(_+)f", "baz")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接