Как заменить набор токенов в Java String?
У меня есть следующий шаблон String: "Hello [Name] Please find attached [Invoice Number] which is due on [Due Date]"
.
У меня также есть переменные String для имени, номера счета и срока действия - какой лучший способ заменить токены в шаблоне переменными?
(Обратите внимание, что если переменная содержит токен, она НЕ должна быть заменена).
ИЗМЕНИТЬ
Благодаря @laginimaineb и @alan-moore, здесь мое решение:
public static String replaceTokens(String text,
Map<String, String> replacements) {
Pattern pattern = Pattern.compile("\\[(.+?)\\]");
Matcher matcher = pattern.matcher(text);
StringBuffer buffer = new StringBuffer();
while (matcher.find()) {
String replacement = replacements.get(matcher.group(1));
if (replacement != null) {
// matcher.appendReplacement(buffer, replacement);
// see comment
matcher.appendReplacement(buffer, "");
buffer.append(replacement);
}
}
matcher.appendTail(buffer);
return buffer.toString();
}
Ответы
Ответ 1
Самый эффективный способ - использовать помощник, чтобы постоянно находить выражения и заменять их, а затем добавлять текст в построитель строк:
Pattern pattern = Pattern.compile("\\[(.+?)\\]");
Matcher matcher = pattern.matcher(text);
HashMap<String,String> replacements = new HashMap<String,String>();
//populate the replacements map ...
StringBuilder builder = new StringBuilder();
int i = 0;
while (matcher.find()) {
String replacement = replacements.get(matcher.group(1));
builder.append(text.substring(i, matcher.start()));
if (replacement == null)
builder.append(matcher.group(0));
else
builder.append(replacement);
i = matcher.end();
}
builder.append(text.substring(i, text.length()));
return builder.toString();
Ответ 2
Я действительно не думаю, что вам нужно использовать механизм шаблонов или что-то в этом роде для этого. Вы можете использовать метод String.format
, например:
String template = "Hello %s Please find attached %s which is due on %s";
String message = String.format(template, name, invoiceNumber, dueDate);
Ответ 3
Вы можете попробовать использовать библиотеку шаблонов, такую как скорость Apache.
http://velocity.apache.org/
Вот пример:
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import java.io.StringWriter;
public class TemplateExample {
public static void main(String args[]) throws Exception {
Velocity.init();
VelocityContext context = new VelocityContext();
context.put("name", "Mark");
context.put("invoiceNumber", "42123");
context.put("dueDate", "June 6, 2009");
String template = "Hello $name. Please find attached invoice" +
" $invoiceNumber which is due on $dueDate.";
StringWriter writer = new StringWriter();
Velocity.evaluate(context, writer, "TemplateName", template);
System.out.println(writer);
}
}
Вывод будет:
Hello Mark. Please find attached invoice 42123 which is due on June 6, 2009.
Ответ 4
К сожалению, удобный метод String.format, упомянутый выше, доступен только с Java 1.5 (который в наши дни должен быть довольно стандартным, но вы никогда не знаете). Вместо этого вы можете также использовать Java класс MessageFormat для замены заполнителей.
Он поддерживает заполнители в форме '{number}', поэтому ваше сообщение будет выглядеть так: "Привет {0}. Приложите {1}, который должен быть {2}". Эти строки могут быть легко реализованы с помощью ResourceBundles (например, для локализации с несколькими локалями). Замена будет выполнена с использованием метода static'format класса MessageFormat:
String msg = "Hello {0} Please find attached {1} which is due on {2}";
String[] values = {
"John Doe", "invoice #123", "2009-06-30"
};
System.out.println(MessageFormat.format(msg, values));
Ответ 5
Вы можете использовать библиотеку шаблонов для сложной замены шаблона.
FreeMarker - очень хороший выбор.
http://freemarker.sourceforge.net/
Но для простой задачи вам может помочь простой класс утилиты.
org.apache.commons.lang3.text.StrSubstitutor
Он очень мощный, настраиваемый и простой в использовании.
Этот класс берет фрагмент текста и заменяет все переменные внутри. Определение переменной по умолчанию - ${variableName}. Префикс и суффикс могут быть изменены с помощью конструкторов и методов набора.
Переменные значения обычно разрешаются с карты, но также могут быть разрешено из свойств системы или путем предоставления настраиваемой переменной распознаватель.
Например, если вы хотите заменить переменную системной среды на строку шаблона,
вот код:
public class SysEnvSubstitutor {
public static final String replace(final String source) {
StrSubstitutor strSubstitutor = new StrSubstitutor(
new StrLookup<Object>() {
@Override
public String lookup(final String key) {
return System.getenv(key);
}
});
return strSubstitutor.replace(source);
}
}
Ответ 6
System.out.println(MessageFormat.format("Hello {0}! You have {1} messages", "Join",10L));
Вывод:
Привет, присоединяйтесь! У вас есть 10 сообщений "
Ответ 7
String.format("Hello %s Please find attached %s which is due on %s", name, invoice, date)
Ответ 8
Это зависит от того, где находятся фактические данные, которые вы хотите заменить. У вас может быть такая Карта:
Map<String, String> values = new HashMap<String, String>();
содержащий все данные, которые можно заменить. Затем вы можете выполнить итерацию по карте и изменить все в строке следующим образом:
String s = "Your String with [Fields]";
for (Map.Entry<String, String> e : values.entrySet()) {
s = s.replaceAll("\\[" + e.getKey() + "\\]", e.getValue());
}
Вы также можете перебирать строку и находить элементы на карте. Но это немного сложнее, потому что вам нужно проанализировать поиск в String для []. Вы можете сделать это с помощью регулярного выражения с помощью Pattern и Matcher.
Ответ 9
Мое решение для замены токенов стиля ${variable} (на основе ответов здесь и Spring UriTemplate):
public static String substituteVariables(String template, Map<String, String> variables) {
Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}");
Matcher matcher = pattern.matcher(template);
// StringBuilder cannot be used here because Matcher expects StringBuffer
StringBuffer buffer = new StringBuffer();
while (matcher.find()) {
if (variables.containsKey(matcher.group(1))) {
String replacement = variables.get(matcher.group(1));
// quote to work properly with $ and {,} signs
matcher.appendReplacement(buffer, replacement != null ? Matcher.quoteReplacement(replacement) : "null");
}
}
matcher.appendTail(buffer);
return buffer.toString();
}
Ответ 10
http://github.com/niesfisch/tokenreplacer
Ответ 11
FYI
В новом языке Котлин,
вы можете использовать "String Templates" в вашем исходном коде напрямую,
никакая сторонняя библиотека или механизм шаблонов не должны выполнять замену переменных.
Это особенность самого языка.
См:
https://kotlinlang.org/docs/reference/basic-types.html#string-templates
Ответ 12
В прошлом я решил эту проблему с StringTemplate и Groovy Шаблоны.
В конечном счете, решение использовать механизм шаблонов или нет должно основываться на следующих факторах:
- Будет ли у вас много таких шаблонов в приложении?
- Вам нужна возможность изменять шаблоны без перезапуска приложения?
- Кто будет поддерживать эти шаблоны? Java-программист или бизнес-аналитик, участвующие в проекте?
- Вам понадобится возможность поместить логику в свои шаблоны, например условный текст, основанный на значениях в переменных?
- Вам нужно включить другие шаблоны в шаблон?
Если какое-либо из вышеперечисленных требований относится к вашему проекту, я бы рассмотрел использование механизма шаблонов, большинство из которых обеспечивают эту функциональность и многое другое.
Ответ 13
Я использовал
String template = "Hello %s Please find attached %s which is due on %s";
String message = String.format(template, name, invoiceNumber, dueDate);
Ответ 14
С библиотекой Apache Commons вы можете просто использовать Stringutils.replaceEach:
public static String replaceEach(String text,
String[] searchList,
String[] replacementList)
Из документация:
Заменяет все вхождения строк в другой строке.
Нулевая ссылка, переданная этому методу, - это no-op или if any search строка "или" строка для замены "имеет значение null, эта замена будет проигнорирована. Это не повторится. Для повторения замещений вызовите перегруженный Метод.
StringUtils.replaceEach(null, *, *) = null
StringUtils.replaceEach("", *, *) = ""
StringUtils.replaceEach("aba", null, null) = "aba"
StringUtils.replaceEach("aba", new String[0], null) = "aba"
StringUtils.replaceEach("aba", null, new String[0]) = "aba"
StringUtils.replaceEach("aba", new String[]{"a"}, null) = "aba"
StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}) = "b"
StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}) = "aba"
StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}) = "wcte"
(example of how it does not repeat)
StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}) = "dcte"