Java正则表达式查找和替换

17
我正在尝试查找输入中的环境变量并将它们替换为其对应的值。
环境变量的模式是${\.}
Pattern myPattern = Pattern.compile( "(${\\.})" );
String line ="${env1}sojods${env2}${env3}";

我该如何将env1替换为1,将env2替换为2,将env3替换为3,以便最终得到新字符串1sojods23


дҪ зҡ„дҫӢеӯҗиЎЁжҳҺпјҢжЁЎејҸеә”иҜҘжҳҜ${[^}]+}жҲ–зұ»дјјзҡ„пјҢиҖҢдёҚжҳҜ${\\.}гҖӮ - Michał Kosmulski
目标是将 'envN' 折叠成 'N',还是用环境/系统属性中分配给 envN 的值替换 'envN'? - John Haager
7个回答

65

Java中的字符串是不可变的,如果您要查找和替换任意数量的内容,则会有一些棘手的问题。

具体而言,您需要在Map中定义替换内容,使用StringBuilder(在Java 9之前,应使用性能较低的StringBuffer),以及来自MatcherappendReplacements()appendTail()方法。最终结果将存储在您的StringBuilder(或StringBuffer)中。

Map<String, String> replacements = new HashMap<String, String>() {{
    put("${env1}", "1");
    put("${env2}", "2");
    put("${env3}", "3");
}};

String line ="${env1}sojods${env2}${env3}";
String rx = "(\\$\\{[^}]+\\})";

StringBuilder sb = new StringBuilder(); //use StringBuffer before Java 9
Pattern p = Pattern.compile(rx);
Matcher m = p.matcher(line);

while (m.find())
{
    // Avoids throwing a NullPointerException in the case that you
    // Don't have a replacement defined in the map for the match
    String repString = replacements.get(m.group(1));
    if (repString != null)    
        m.appendReplacement(sb, repString);
}
m.appendTail(sb);

System.out.println(sb.toString());

输出:

1sojods23

1
优美而优雅...我曾经尝试使用基本字符串操作来完成同样的任务,但发现我的代码很丑陋...现在我也用Map等方法来替换${variables}...是时候对我的代码进行一些清理了。 - YoYo
将例如这个字符串:"$#,##0.0" 替换为 "$'#',##0.0",可以使用 Matcher.quoteReplacement(...) 方法。代码如下:if (replace != null) { String rp = Matcher.quoteReplacement("'" + replace + "'"); matcher.appendReplacement(sb, rp); } - Hank Moody

12

我知道这已经过时了。当我在寻找appendReplacement/appendTail示例时,我自己找到了它;然而,楼主的问题并不需要我在这里看到的那些复杂的多行解决方案。

在这种特定情况下,当要替换的字符串本身就是我们想要替换的值时,可以轻松地使用replaceAll来实现:

String line ="${env1}sojods${env2}${env3}";

System.out.println( line.replaceAll("\\$\\{env([0-9]+)\\}", "$1") );

// Output => 1sojods23

演示

当替换是基于某些条件或逻辑的随机匹配时,你可以使用appendReplacement/appendTail方法。


6
希望你会发现这段代码有用:

希望你会发现这段代码有用:

    Pattern phone = Pattern.compile("\\$\\{env([0-9]+)\\}");
    String line ="${env1}sojods${env2}${env3}";
    Matcher action = phone.matcher(line);
    StringBuffer sb = new StringBuffer(line.length());
    while (action.find()) {
      String text = action.group(1);
      action.appendReplacement(sb, Matcher.quoteReplacement(text));
    }
    action.appendTail(sb);
    System.out.println(sb.toString());

输出结果应为: 1sojods23

变量名怎么样,是env_a、env_b和env_c? - Kent
好的,没问题,组的模式可以更改。我只关注于OP提出的非常具体的问题,但我猜他可以相应地更改模式(毕竟没有给出良好的规范)。 - Boris Strandjev
特别感谢,非常感谢您的快速回答,这真是一个很棒的答案。 - user1205079

5
这会给你一个1sojods23
String s = "${env1}sojods${env2}${env3}";
final Pattern myPattern = Pattern.compile("\\$\\{[^\\}]*\\}");
Matcher m = myPattern.matcher(s);
int i = 0;
while (m.find()) {
    s = m.replaceFirst(String.valueOf(++i));
    m = myPattern.matcher(s);
}

System.out.println(s);

这个也可以运作:

final String re = "\\$\\{[^\\}]*\\}";
String s = "${env1}sojods${env2}${env3}";
int i = 0;
String t;
while (true) {
    t = s.replaceFirst(re, String.valueOf(++i));
    if (s.equals(t)) {
        break;
    } else {
        s = t;
    }
}

System.out.println(s);

0

你可以使用StringBuffer与Matcher的appendReplacement()方法结合使用,但如果模式不匹配,则没有必要创建StringBuffer。

例如,这里有一个匹配${...}的模式。第1组是大括号之间的内容。

static Pattern rxTemplate = Pattern.compile("\\$\\{([^}\\s]+)\\}");

这里是一个使用该模式的示例函数。

private static String replaceTemplateString(String text) {
    StringBuffer sb = null;
    Matcher m = rxTemplate.matcher(text);
    while (m.find()) {
        String t = m.group(1);
        t = t.toUpperCase(); // LOOKUP YOUR REPLACEMENT HERE

        if (sb == null) {
            sb = new StringBuffer(text.length());
        }
        m.appendReplacement(sb, t);
    }
    if (sb == null) {
        return text;
    } else {
        m.appendTail(sb);
        return sb.toString();
    }
}

0
    Map<String, String> replacements = new HashMap<String, String>() {
        {
            put("env1", "1");
            put("env2", "2");
            put("env3", "3");

        }
    };

    String line = "${env1}sojods${env2}${env3}";

    String rx = "\\$\\{(.*?)\\}";

    StringBuffer sb = new StringBuffer();
    Pattern p = Pattern.compile(rx);
    Matcher m = p.matcher(line);

    while (m.find()) {
        // Avoids throwing a NullPointerException in the case that you
        // Don't have a replacement defined in the map for the match
        String repString = replacements.get(m.group(1));
        if (repString != null)
            m.appendReplacement(sb, repString);
    }
    m.appendTail(sb);

    System.out.println(sb.toString());

在上面的例子中,我们可以只使用键和值来使用 map -- 键可以是 env1、env2 等。

-2

一旦匹配成功,使用分组,${env1} 将是你的第一个分组,然后你可以使用正则表达式替换每个分组中的内容。

Pattern p = Pattern.compile("(${\\.})");
Matcher m = p.matcher(line);
while (m.find())
  for (int j = 0; j <= m.groupCount(); j++)
    //here you do replacement - check on the net how to do it;)

我尝试了这个模式,但是出现了以下异常: Exception in thread "main" java.util.regex.PatternSyntaxException: Illegal repetition near index 1 (${.}) - user1205079

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