Strsubstitutor замена библиотек JRE

на данный момент я использую 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}.");

уступая resolvedString = "быстрая коричневая лиса перепрыгнула через ленивую собаку."

2 ответов


если производительность не является приоритетом, можно использовать 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();
    }

это примерно 2x так же быстро, как версия regex и 3X быстрее, чем версия Apache commons В соответствии с моими тестами. Таким образом, обычный материал regex на самом деле более оптимизирован, чем версия apache. Обычно, конечно, не стоит. Просто для удовольствия, дайте мне знать, если вы можете сделать его более оптимизированным.

редактировать: как указывает @kmek, есть оговорка. Версия Apache будет разрешаться транзитивно. е.G, Если ${animal} карты ${dog} и dog карты Golden Retriever, версия apache будет отображать ${animal} Золотой ретривер. Как я уже сказал, Вы должны использовать библиотеки, насколько это возможно. Вышеуказанное решение должно использоваться только в том случае, если у вас есть специальное ограничение, которое не позволяет использовать библиотеку.


нет ничего подобного, что я знаю в 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 все время