9

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

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

result = substitutor.replace(input);

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

注意

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 = "那只敏捷的棕色狐狸跳过了那只懒狗。"

4

2 回答 2

14

如果性能不是优先级,您可以使用类的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();
    }
}

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

    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();
    }

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

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

于 2012-12-26T19:47:35.853 回答
1

我知道 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();
}

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

于 2012-12-26T18:49:39.000 回答