java 用 JRE 库替换 StrSubstitutor

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14044715/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 14:51:55  来源:igfitidea点击:

StrSubstitutor replacement with JRE libraries

stringtextjava

提问by Luigi R. Viggiano

At the moment I am using org.apache.commons.lang.text.StrSubstitutorfor doing:

目前我正在使用org.apache.commons.lang.text.StrSubstitutor

Map m = ...
substitutor = new StrSubstitutor(m);

result = substitutor.replace(input);

Given the fact I want to remove commons-langdependency from my project what would be a working and minimalistic implementation of StrSubstitutorusing standard JRE libraries?

鉴于我想commons-lang从我的项目中删除依赖项,StrSubstitutor使用标准 JRE 库的工作和简约实现是什么?

Note:

注意

StrSubstitutorworks like this:

StrSubstitutor像这样工作:

Map map = new HashMap();
map.put("animal", "quick brown fox");
map.put("target", "lazy dog");
StrSubstitutor sub = new StrSubstitutor(map);
String resolvedString = sub.replace("The ${animal} jumped over the ${target}.");

yielding resolvedString = "The quick brown fox jumped over the lazy dog."

产生 resolveString = "敏捷的棕色狐狸跳过了懒惰的狗。"

回答by Hari Menon

If performance is not a priority,you can use the appendReplacementmethod of the Matcherclass:

如果性能不是优先事项,您可以使用appendReplacement方法Matcher

public class StrSubstitutor {
    private Map<String, String> map;
    private static final Pattern p = Pattern.compile("\$\{(.+?)\}");

    public StrSubstitutor(Map<String, String> map) {
        this.map = map;
    }

    public String replace(String str) {
        Matcher m = p.matcher(str);
        StringBuilder sb = new StringBuilder();
        while (m.find()) {
            String var = m.group(1);
            String replacement = map.get(var);
            m.appendReplacement(sb, replacement);
        }
        m.appendTail(sb);
        return sb.toString();
    }
}

A more performant but uglier version, just for fun :)

性能更高但更丑的版本,只是为了好玩:)

    public String replace(String str) {
        StringBuilder sb = new StringBuilder();
        char[] strArray = str.toCharArray();
        int i = 0;
        while (i < strArray.length - 1) {
            if (strArray[i] == '$' && strArray[i + 1] == '{') {
                i = i + 2;
                int begin = i;
                while (strArray[i] != '}') ++i;
                sb.append(map.get(str.substring(begin, i++)));
            } else {
                sb.append(strArray[i]);
                ++i;
            }
        }
        if (i < strArray.length) sb.append(strArray[i]);
        return sb.toString();
    }

It's about 2x as fast as the regex version and 3x faster than the apache commons version as per my tests. So the normal regex stuff is actually more optimized than the apache version. Usually not worth it of course. Just for fun though, let me know if you can make it more optimized.

根据我的测试,它大约是 regex 版本的 2 倍,比 apache commons 版本快 3 倍。所以正常的正则表达式实际上比 apache 版本更优化。通常当然不值得。只是为了好玩,让我知道你是否可以让它更优化。

Edit: As @kmek points out, there is a caveat. Apache version will resolve transitively. e.g, If ${animal}maps to ${dog}and dogmaps to Golden Retriever, apache version will map ${animal}to Golden Retriever. As I said, you should use libraries as far as possible. The above solution is only to be used if you have a special constraint which does not allow you to use a library.

编辑:正如@kmek 指出的那样,有一个警告。Apache 版本将传递解析。例如,如果${animal}映射到${dog}dog映射到Golden Retriever,apache 版本将映射${animal}到金毛猎犬。正如我所说,您应该尽可能使用库。仅当您有不允许使用库的特殊约束时才使用上述解决方案。

回答by radai

there's nothing like this that i know of in the JRE, but writing one is simple enough.

我在 JRE 中不知道这样的事情,但写一个很简单。

Pattern p = Pattern.compile("${([a-zA-Z]+)}";
Matcher m = p.matcher(inputString);
int lastEnd = -1;
while (m.find(lastEnd+1)) {
   int startIndex = m.start();
   String varName = m.group(1);
   //lookup value in map and substitute
   inputString = inputString.substring(0,m.start())+replacement+inputString.substring(m.end());
   lastEnt = m.start() + replacement.size();
}

this is of course horribly inefficient and you should probably write the result into a StringBuilder instead of replacing inputString all the time

这当然非常低效,您可能应该将结果写入 StringBuilder 而不是一直替换 inputString