Замена строки в java, аналогично шаблону скорости

96

Есть ли Stringв Java механизм замены, где я могу передавать объекты с текстом, и он заменяет строку по мере ее появления.
Например, текст такой:

Hello ${user.name},
    Welcome to ${site.name}. 

У меня есть предметы "user"и "site". Я хочу заменить строки, указанные внутри, ${}эквивалентными значениями из объектов. Это то же самое, что мы заменяем объекты в шаблоне скорости.

Джо
источник
1
Заменить где? Класс? JSP? String имеет метод форматирования, если вы просто:String.format("Hello %s", username);
Droo
1
@Droo: В этом примере строка похожа Hello ${user.name}, а не похожа Hello %sили Hello {0}.
Адил Ансари,
2
Если вам нужно что-то похожее на скорость и пахнущее скоростью, может быть, это скорость? :)
serg 07
@Droo: Это не класс. У меня есть приведенный выше текст в переменной «String», и я хочу заменить все вхождения строк внутри $ {} значениями в соответствующих объектах. например, замените все $ {user.name} свойством name в объекте "user".
Джо,
@serg: Да, это код скорости. и я хочу убрать скорость из моего кода.
Джо

Ответы:

142

Используйте StringSubstitutorиз текста Apache Commons.

https://commons.apache.org/proper/commons-text/

Он сделает это за вас (и его открытый исходный код ...)

 Map<String, String> valuesMap = new HashMap<String, String>();
 valuesMap.put("animal", "quick brown fox");
 valuesMap.put("target", "lazy dog");
 String templateString = "The ${animal} jumped over the ${target}.";
 StringSubstitutor sub = new StringSubstitutor(valuesMap);
 String resolvedString = sub.replace(templateString);
JH.
источник
4
Документация Javadoc для StrSubstitutor commons.apache.org/lang/api-release/org/apache/commons/lang/…
Пол
1
Должно быть Map<String, String> valuesMap = new HashMap<String, String>();.
andrewrjones
3
StrSubstitutorтеперь устарела в https://commons.apache.org/proper/commons-lang/ . Пользователь https://commons.apache.org/proper/commons-text/ вместо этого
Лукулуба
7
StrSubstitutorустарело с 1.3, используйте StringSubstitutorвместо него. Этот класс будет удален в 2.0. Gradle зависимость для импортирования StringSubstitutorявляетсяorg.apache.commons:commons-text:1.4
Юрий Rabeshko
допускает ли замену на основе условий?
Gaurav
131

Взгляните на java.text.MessageFormatкласс: MessageFormat принимает набор объектов, форматирует их, а затем вставляет отформатированные строки в шаблон в соответствующих местах.

Object[] params = new Object[]{"hello", "!"};
String msg = MessageFormat.format("{0} world {1}", params);
RealHowTo
источник
10
Спасибо! Я знал, что в java должен быть встроенный способ сделать это без необходимости использовать этот чертов шаблонный движок, чтобы сделать такую ​​простую вещь!
Джозеф Раджив Мота
2
Кажется , как string.Format может сделать что - нибудь это может сделать - stackoverflow.com/questions/2809633/...
ноумен
6
+1. Имейте formatв Object...виду, также принимает varargs, поэтому вы можете использовать этот более сжатый синтаксис там, где это предпочтительнееformat("{0} world {1}", "Hello", "!");
davnicwil
Следует отметить, что его MessageFormatможно надежно использовать только для его тезки, отображения сообщений, а не для вывода, когда имеет значение техническое форматирование. Например, числа будут отформатированы в соответствии с настройками локали, что сделает их недействительными для технических целей.
Марн
24

Я предпочитаю, String.format()потому что это один лайнер и не требует сторонних библиотек:

String message = String.format("Hello! My name is %s, I'm %s.", name, age); 

Я использую это регулярно, например, в сообщениях об исключениях, например:

throw new Exception(String.format("Unable to login with email: %s", email));

Подсказка: вы можете ввести столько переменных, сколько захотите, потому что format()использует Varargs

artgrohe
источник
Это менее полезно, если вам нужно повторить один и тот же аргумент более одного раза. Например: String.format("Hello! My name is %s, I'm %s. Why is my name %s you ask? Well I'm only %s years old so I don't know", name, age, name, age);. Другие ответы здесь требуют указания каждого аргумента только один раз.
Ашербар
2
@asherbar вы можете использовать спецификаторы индекса аргумента в строке формата, напримерString.format("Hello! My name is %1$s, I'm %2$s. Why is my name %1$s you ask? Well I'm only %2$s years old so I don't know", name, age)
jazzpi
@jazzpi Я этого не знал. Спасибо!
Asherbar
20

Я собрал небольшую тестовую реализацию этого. Основная идея - позвонитьformat и передать строку формата, карту объектов и имена, которые они имеют локально.

Результатом будет следующее:

Моего пса зовут Фидо, и он принадлежит Джейн Доу.

public class StringFormatter {

    private static final String fieldStart = "\\$\\{";
    private static final String fieldEnd = "\\}";

    private static final String regex = fieldStart + "([^}]+)" + fieldEnd;
    private static final Pattern pattern = Pattern.compile(regex);

    public static String format(String format, Map<String, Object> objects) {
        Matcher m = pattern.matcher(format);
        String result = format;
        while (m.find()) {
            String[] found = m.group(1).split("\\.");
            Object o = objects.get(found[0]);
            Field f = o.getClass().getField(found[1]);
            String newVal = f.get(o).toString();
            result = result.replaceFirst(regex, newVal);
        }
        return result;
    }

    static class Dog {
        public String name;
        public String owner;
        public String gender;
    }

    public static void main(String[] args) {
        Dog d = new Dog();
        d.name = "fido";
        d.owner = "Jane Doe";
        d.gender = "him";
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("d", d);
        System.out.println(
           StringFormatter.format(
                "My dog is named ${d.name}, and ${d.owner} owns ${d.gender}.", 
                map));
    }
}

Примечание: это не компилируется из-за необработанных исключений. Но это значительно упрощает чтение кода.

Кроме того, мне не нравится, что вы должны сами создавать карту в коде, но я не знаю, как получить имена локальных переменных программно. Лучший способ сделать это - не забыть поместить объект на карту сразу после его создания.

Следующий пример дает результаты, которые вы хотите получить из своего примера:

public static void main(String[] args) {
    Map<String, Object> map = new HashMap<String, Object>();
    Site site = new Site();
    map.put("site", site);
    site.name = "StackOverflow.com";
    User user = new User();
    map.put("user", user);
    user.name = "jjnguy";
    System.out.println(
         format("Hello ${user.name},\n\tWelcome to ${site.name}. ", map));
}

Я также должен упомянуть, что я понятия не имею, что такое скорость, поэтому я надеюсь, что этот ответ актуален.

jjnguy
источник
Это то, что я искал. Спасибо за реализацию. Я пытался и получал неверные результаты. : D. В любом случае это решило мою проблему.
Джо,
2
@ Джо, рад, что смог помочь. Для меня это был хороший повод, наконец, попрактиковаться в написании кода, использующего отражение на Java.
jjnguy
6

Вот схема того, как вы могли бы это сделать. Реализовать его как реальный код должно быть относительно просто.

  1. Создайте карту всех объектов, на которые будут ссылаться в шаблоне.
  2. Используйте регулярное выражение, чтобы найти ссылки на переменные в шаблоне и заменить их их значениями (см. Шаг 3). Класс Matcher пригодится для поиска и замены.
  3. Разделите имя переменной по точкам. user.nameстал бы userи name. Посмотрите userна карту, чтобы получить объект, и используйте отражение, чтобы получить значение nameот объекта. Предполагая, что у ваших объектов есть стандартные геттеры, вы будете искать метод getNameи вызывать его.
касабланка
источник
Хех, только что видел этот ответ. Он идентичен моему. Пожалуйста, дайте мне знать, что вы думаете о моей реализации.
jjnguy
5

Есть несколько реализаций Expression Language, которые делают это за вас, может быть предпочтительнее использовать вашу собственную реализацию по мере роста ваших требований, см., Например, JUEL и MVEL

Мне нравится и я успешно использовал MVEL как минимум в одном проекте.

Также см. Сообщение Stackflow JSTL / JSP EL (язык выражений) в контексте, отличном от JSP (автономный).

Кристоффер Суп
источник
0

Нет ничего стандартного, что можно было бы сравнить со скоростью, поскольку скорость была написана для решения именно этой задачи. Самое близкое, что вы можете попробовать, - это заглянуть в Formatter.

http://cupi2.uniandes.edu.co/site/images/recursos/javadoc/j2se/1.5.0/docs/api/java/util/Formatter.html

Однако, насколько мне известно, средство форматирования было создано для предоставления C-подобных параметров форматирования в Java, поэтому он может не совсем поцарапать ваш зуд, но вы можете попробовать :).

Майк Милкин
источник
0

Я использую GroovyShell в java для синтаксического анализа шаблона с помощью Groovy GString:

Binding binding = new Binding();
GroovyShell gs = new GroovyShell(binding);
// this JSONObject can also be replaced by any Java Object
JSONObject obj = new JSONObject();
obj.put("key", "value");
binding.setProperty("obj", obj)
String str = "${obj.key}";
String exp = String.format("\"%s\".toString()", str);
String res = (String) gs.evaluate(exp);
// value
System.out.println(str);
Кан
источник