Іменовані заповнювачі у форматі рядків


175

У Python під час форматування рядка я можу заповнювати заповнювачі за іменем, а не за позицією, наприклад:

print "There's an incorrect value '%(value)s' in column # %(column)d" % \
  { 'value': x, 'column': y }

Цікаво, чи можливо це на Java (сподіваємось, без зовнішніх бібліотек)?


Ви можете розширити MessageFormat і застосувати функціональність відображення від змінних до індексів у цьому.
впрам86


1
Деяка історія: Java здебільшого копіювала C / C ++ з цього питання, оскільки вона намагалася заманювати розробників зі світу C ++, де це %sбуло звичайною практикою. en.wikipedia.org/wiki/Printf_format_string#History Також зауважте, що деякі IDE та FindBugs можуть автоматично виявити невідповідність підрахунків% s та% d, але я все-таки вважаю за краще названі поля.
Крістоф Руссі

Відповіді:


143

StrSubstitutor jakarta commons lang - це легкий спосіб зробити це за умови, що ваші значення вже правильно відформатовані.

http://commons.apache.org/proper/commons-lang/javadocs/api-3.1/org/apache/commons/lang3/text/StrSubstitutor.html

Map<String, String> values = new HashMap<String, String>();
values.put("value", x);
values.put("column", y);
StrSubstitutor sub = new StrSubstitutor(values, "%(", ")");
String result = sub.replace("There's an incorrect value '%(value)' in column # %(column)");

Вищезазначене призводить до:

"У стовпці №2 неправильне значення" 1 "

Під час використання Maven ви можете додати цю залежність до свого pom.xml:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.4</version>
</dependency>

2
Мені було прикро, що бібліотека не кидає, якщо ключі не знайдені, однак, якщо ви використовуєте синтаксис за замовчуванням ( ${arg}) замість спеціального вище ( %(arg)), то регулярний вираз не буде компілюватися, що є бажаним ефектом.
Джон Леманн

2
Ви можете встановити спеціальний VariableResolver і викинути Виняток, якщо ключ не присутній на карті.
Мене

7
Старий потік, але станом на 3.6, текстовий пакет був застарілий на користь commons-text. commons.apache.org/proper/commons-text
Джефф Уокер

74

не зовсім, але ви можете використовувати MessageFormat для посилання на одне значення кілька разів:

MessageFormat.format("There's an incorrect value \"{0}\" in column # {1}", x, y);

Вищезазначене можна зробити і з String.format (), але я знаходжу очищення синтаксису messageFormat, якщо вам потрібно створити складні вирази, плюс вам не потрібно дбати про тип об'єкта, який ви вводите в рядок


не впевнений, чому ви не можете, позиція в рядку не важлива, лише позиція у списку аргументів, що робить її проблемою перейменування. Ви знаєте назву ключів, а значить, ви можете визначити позицію для ключа у списку аргументів. відтепер значення буде відоме як 0, а стовпець - 1: MessageeFormat.format ("У стовпці № {1} неправильне значення \" {0} \ "; використання {0} як значення може спричинити багато проблем", valueMap .get ('value'), valueMap.get ('стовпець'));
giladbu

1
Дякую за підказку, вона допомогла мені написати просту функцію, яка виконує саме те, що я хочу (я поставив її нижче).
Енді

1
Погоджено, синтаксис набагато чистіший. Надто поганий MessageFormat має власний розум, коли справа доходить до форматування числових значень.
Кіс де Коотер

І, здається, ігноруйте заповнювачі, оточені єдиними цитатами.
Kees de Kooter

MessageFormatчудово, але громіздко для відносно великого вмісту json
EliuX

32

Ще один приклад Apache Common StringSubstitutor для простого названого заповнювача.

String template = "Welcome to {theWorld}. My name is {myName}.";

Map<String, String> values = new HashMap<>();
values.put("theWorld", "Stackoverflow");
values.put("myName", "Thanos");

String message = StringSubstitutor.replace(template, values, "{", "}");

System.out.println(message);

// Welcome to Stackoverflow. My name is Thanos.

Якщо ви очікуєте завантаження дуже великих файлів, я знайшов, що ця бібліотека також підтримує, replaceInяка замінює значення в буфер: StringBuilder або TextStringBuilder. При такому підході весь вміст файлу не завантажується в пам'ять.
Едвард Коррігалл

15

Ви можете використовувати бібліотеку StringTemplate , вона пропонує те, що ви хочете, і багато іншого.

import org.antlr.stringtemplate.*;

final StringTemplate hello = new StringTemplate("Hello, $name$");
hello.setAttribute("name", "World");
System.out.println(hello.toString());

Мали проблеми з 'unexpected char: '''
чаром

11

Для дуже простих випадків ви можете просто замінити рядок із жорстким кодом, немає необхідності в бібліотеці:

    String url = "There's an incorrect value '%(value)' in column # %(column)";
    url = url.replace("%(value)", x); // 1
    url = url.replace("%(column)", y); // 2

Попередження : Я просто хотів показати найпростіший можливий код. Звичайно, НЕ використовуйте це для серйозного виробничого коду, коли питання безпеки, як зазначено в коментарях: тут є проблемою втечі, обробки помилок та безпеки. Але в найгіршому випадку ви тепер знаєте, чому використовувати «добру» в’язку потрібно :-)


1
цей простий і простий, але недоліком є ​​те, що він виходить з ладу, коли значення не було знайдено. Він просто залишає заповнювач місця в початковому рядку.
kiedysktos

@kiedysktos, ви могли б покращити це, зробивши перевірку, але якщо ви хочете повністю, скористайтеся lib :)
Крістоф Руссі

2
Попередження: Оскільки ця методика розглядає результати проміжних підстановок як власні рядки формату, це рішення є вразливим для форматних атак . Будь-яке правильне рішення повинне зробити один прохід через рядок формату.
200_успіх

@ 200_success Так гарно говорити про безпеку, звичайно, цей код не для серйозного використання виробництва ...
Крістоф Руссі

8

Дякуємо за всю вашу допомогу! Використовуючи всі ваші підказки, я написав рутину, щоб робити саме те, що мені хочеться - формат рядків у формі пітона як словник. Оскільки я новачок на Java, будь-які підказки цінуються.

public static String dictFormat(String format, Hashtable<String, Object> values) {
    StringBuilder convFormat = new StringBuilder(format);
    Enumeration<String> keys = values.keys();
    ArrayList valueList = new ArrayList();
    int currentPos = 1;
    while (keys.hasMoreElements()) {
        String key = keys.nextElement(),
        formatKey = "%(" + key + ")",
        formatPos = "%" + Integer.toString(currentPos) + "$";
        int index = -1;
        while ((index = convFormat.indexOf(formatKey, index)) != -1) {
            convFormat.replace(index, index + formatKey.length(), formatPos);
            index += formatPos.length();
        }
        valueList.add(values.get(key));
        ++currentPos;
    }
    return String.format(convFormat.toString(), valueList.toArray());
}

На відміну від відповіді Ломбо, це не може застрягти у нескінченному циклі, оскільки formatPosне може містити його formatKey.
Аарон Дюфур

6
Попередження: Оскільки цикл розглядає результати проміжних підстановок як власні рядки формату, це рішення є вразливим для форматних атак . Будь-яке правильне рішення повинне зробити один прохід через рядок формату.
200_успіх

6

Це стара нитка, але тільки для запису ви також можете використовувати стиль Java 8, наприклад:

public static String replaceParams(Map<String, String> hashMap, String template) {
    return hashMap.entrySet().stream().reduce(template, (s, e) -> s.replace("%(" + e.getKey() + ")", e.getValue()),
            (s, s2) -> s);
}

Використання:

public static void main(String[] args) {
    final HashMap<String, String> hashMap = new HashMap<String, String>() {
        {
            put("foo", "foo1");
            put("bar", "bar1");
            put("car", "BMW");
            put("truck", "MAN");
        }
    };
    String res = replaceParams(hashMap, "This is '%(foo)' and '%(foo)', but also '%(bar)' '%(bar)' indeed.");
    System.out.println(res);
    System.out.println(replaceParams(hashMap, "This is '%(car)' and '%(foo)', but also '%(bar)' '%(bar)' indeed."));
    System.out.println(replaceParams(hashMap, "This is '%(car)' and '%(truck)', but also '%(foo)' '%(bar)' + '%(truck)' indeed."));
}

Вихід буде:

This is 'foo1' and 'foo1', but also 'bar1' 'bar1' indeed.
This is 'BMW' and 'foo1', but also 'bar1' 'bar1' indeed.
This is 'BMW' and 'MAN', but also 'foo1' 'bar1' + 'MAN' indeed.

Це геніально, але, на жаль, порушує специфікації тут docs.oracle.com/javase/8/docs/api/java/util/stream/… Функція комбінатора повинна повертати другий параметр, якщо перший параметр є ідентичністю. Наведений вище замість цього повертає особу. Він також порушує це правило: combiner.apply (u ,umulator.apply (особистість, t)) ==
umulator.apply

Цікаво ... але тільки якщо ви пропонуєте приємніший спосіб передати карту, також, якщо можливо, за шаблоном, як і більшість кодів форматування.
Крістоф Руссі

4
Попередження: Оскільки результати .reduce()проміжних підстановок трактуються як власні рядки формату, це рішення є вразливим до форматних атак . Будь-яке правильне рішення повинне зробити один прохід через рядок формату.
200_успіх

6
public static String format(String format, Map<String, Object> values) {
    StringBuilder formatter = new StringBuilder(format);
    List<Object> valueList = new ArrayList<Object>();

    Matcher matcher = Pattern.compile("\\$\\{(\\w+)}").matcher(format);

    while (matcher.find()) {
        String key = matcher.group(1);

        String formatKey = String.format("${%s}", key);
        int index = formatter.indexOf(formatKey);

        if (index != -1) {
            formatter.replace(index, index + formatKey.length(), "%s");
            valueList.add(values.get(key));
        }
    }

    return String.format(formatter.toString(), valueList.toArray());
}

Приклад:

String format = "My name is ${1}. ${0} ${1}.";

Map<String, Object> values = new HashMap<String, Object>();
values.put("0", "James");
values.put("1", "Bond");

System.out.println(format(format, values)); // My name is Bond. James Bond.

2
Це має бути відповіддю, оскільки це дозволяє уникнути атак форматних рядків, до яких вразливі більшість інших рішень. Зауважте, що Java 9 робить це набагато простіше, підтримуючи .replaceAll()зворотні зворотні підстановки підстановки .
200_успіх

Це має бути відповіддю, оскільки він не використовує жодних зовнішніх бібліотек.
Бохао Л.І.

3

Я автор невеликої бібліотеки, яка робить саме те, що ви хочете:

Student student = new Student("Andrei", 30, "Male");

String studStr = template("#{id}\tName: #{st.getName}, Age: #{st.getAge}, Gender: #{st.getGender}")
                    .arg("id", 10)
                    .arg("st", student)
                    .format();
System.out.println(studStr);

Або ви можете зв'язати аргументи:

String result = template("#{x} + #{y} = #{z}")
                    .args("x", 5, "y", 10, "z", 15)
                    .format();
System.out.println(result);

// Output: "5 + 10 = 15"

чи можна виконати формат на основі умов форматів із вашою бібліотекою?
gaurav

@gaurav не зовсім. Якщо вам потрібно, вам потрібна повнофункціональна бібліотека шаблонів.
Андрій Чобану

2

Apache Commons метод заміни Ланга Aach може стати в нагоді залежно від ваших конкретних потреб. Ви можете легко використовувати його для заміни заповнювачів імен по імені за допомогою цього єдиного виклику методу:

StringUtils.replaceEach("There's an incorrect value '%(value)' in column # %(column)",
            new String[] { "%(value)", "%(column)" }, new String[] { x, y });

Враховуючи деякий вхідний текст, це замінить усі входження заповнювачів у першому масиві рядків відповідними значеннями у другому.


1

Ви могли б мати щось подібне на класі струнного помічника

/**
 * An interpreter for strings with named placeholders.
 *
 * For example given the string "hello %(myName)" and the map <code>
 *      <p>Map<String, Object> map = new HashMap<String, Object>();</p>
 *      <p>map.put("myName", "world");</p>
 * </code>
 *
 * the call {@code format("hello %(myName)", map)} returns "hello world"
 *
 * It replaces every occurrence of a named placeholder with its given value
 * in the map. If there is a named place holder which is not found in the
 * map then the string will retain that placeholder. Likewise, if there is
 * an entry in the map that does not have its respective placeholder, it is
 * ignored.
 *
 * @param str
 *            string to format
 * @param values
 *            to replace
 * @return formatted string
 */
public static String format(String str, Map<String, Object> values) {

    StringBuilder builder = new StringBuilder(str);

    for (Entry<String, Object> entry : values.entrySet()) {

        int start;
        String pattern = "%(" + entry.getKey() + ")";
        String value = entry.getValue().toString();

        // Replace every occurence of %(key) with value
        while ((start = builder.indexOf(pattern)) != -1) {
            builder.replace(start, start + pattern.length(), value);
        }
    }

    return builder.toString();
}

Велике спасибі, він робить майже все, що я хочу, але єдине, що він не враховує модифікаторів (врахуйте "% (ключ) 08d")
Енді,

1
Зауважте також, що це переходить у нескінченний цикл, якщо будь-яке з використаних значень містить відповідний запис.
Аарон Дюфур

1
Попередження: Оскільки цикл розглядає результати проміжних підстановок як власні рядки формату, це рішення є вразливим для форматних атак . Будь-яке правильне рішення повинне зробити один прохід через рядок формату.
200_успіх

1

Моя відповідь:

а) використовуйте StringBuilder, коли це можливо

б) збережіть (у будь-якій формі: ціле число - найкраще, специфічне опис, як макро долара тощо) позицію "заповнювача", а потім використовуйте StringBuilder.insert() (кілька версій аргументів).

Використання зовнішніх бібліотек здається непосильним, і я вважаю, що ефективність деградації істотна, коли StringBuilder перетворюється на String внутрішньо.


1

На основі відповіді я створив MapBuilderклас:

public class MapBuilder {

    public static Map<String, Object> build(Object... data) {
        Map<String, Object> result = new LinkedHashMap<>();

        if (data.length % 2 != 0) {
            throw new IllegalArgumentException("Odd number of arguments");
        }

        String key = null;
        Integer step = -1;

        for (Object value : data) {
            step++;
            switch (step % 2) {
                case 0:
                    if (value == null) {
                        throw new IllegalArgumentException("Null key value");
                    }
                    key = (String) value;
                    continue;
                case 1:
                    result.put(key, value);
                    break;
            }
        }

        return result;
    }

}

тоді я створив клас StringFormatдля форматування рядків:

public final class StringFormat {

    public static String format(String format, Object... args) {
        Map<String, Object> values = MapBuilder.build(args);

        for (Map.Entry<String, Object> entry : values.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            format = format.replace("$" + key, value.toString());
        }

        return format;
    }

}

які ви могли використовувати так:

String bookingDate = StringFormat.format("From $startDate to $endDate"), 
        "$startDate", formattedStartDate, 
        "$endDate", formattedEndDate
);

1
Попередження: Оскільки цикл розглядає результати проміжних підстановок як власні рядки формату, це рішення є вразливим для форматних атак . Будь-яке правильне рішення повинне зробити один прохід через рядок формату.
200_успіх

1

Я створив також клас util / helper (використовуючи jdk 8), який може відформатувати рядок і замінює входження змінних.

З цією метою я використав метод Matchers "appendReplacement", який виконує всі підстановки і циклічно лише над ураженими частинами рядка формату.

На сьогоднішній день клас помічників не є добре задокументованим. Я це зміню в майбутньому;) У будь-якому випадку я прокоментував найважливіші рядки (сподіваюся).

    public class FormatHelper {

    //Prefix and suffix for the enclosing variable name in the format string.
    //Replace the default values with any you need.
    public static final String DEFAULT_PREFIX = "${";
    public static final String DEFAULT_SUFFIX = "}";

    //Define dynamic function what happens if a key is not found.
    //Replace the defualt exception with any "unchecked" exception type you need or any other behavior.
    public static final BiFunction<String, String, String> DEFAULT_NO_KEY_FUNCTION =
            (fullMatch, variableName) -> {
                throw new RuntimeException(String.format("Key: %s for variable %s not found.",
                                                         variableName,
                                                         fullMatch));
            };
    private final Pattern variablePattern;
    private final Map<String, String> values;
    private final BiFunction<String, String, String> noKeyFunction;
    private final String prefix;
    private final String suffix;

    public FormatHelper(Map<String, String> values) {
        this(DEFAULT_NO_KEY_FUNCTION, values);
    }

    public FormatHelper(
            BiFunction<String, String, String> noKeyFunction, Map<String, String> values) {
        this(DEFAULT_PREFIX, DEFAULT_SUFFIX, noKeyFunction, values);
    }

    public FormatHelper(String prefix, String suffix, Map<String, String> values) {
        this(prefix, suffix, DEFAULT_NO_KEY_FUNCTION, values);
    }

    public FormatHelper(
            String prefix,
            String suffix,
            BiFunction<String, String, String> noKeyFunction,
            Map<String, String> values) {
        this.prefix = prefix;
        this.suffix = suffix;
        this.values = values;
        this.noKeyFunction = noKeyFunction;

        //Create the Pattern and quote the prefix and suffix so that the regex don't interpret special chars.
        //The variable name is a "\w+" in an extra capture group.
        variablePattern = Pattern.compile(Pattern.quote(prefix) + "(\\w+)" + Pattern.quote(suffix));
    }

    public static String format(CharSequence format, Map<String, String> values) {
        return new FormatHelper(values).format(format);
    }

    public static String format(
            CharSequence format,
            BiFunction<String, String, String> noKeyFunction,
            Map<String, String> values) {
        return new FormatHelper(noKeyFunction, values).format(format);
    }

    public static String format(
            String prefix, String suffix, CharSequence format, Map<String, String> values) {
        return new FormatHelper(prefix, suffix, values).format(format);
    }

    public static String format(
            String prefix,
            String suffix,
            BiFunction<String, String, String> noKeyFunction,
            CharSequence format,
            Map<String, String> values) {
        return new FormatHelper(prefix, suffix, noKeyFunction, values).format(format);
    }

    public String format(CharSequence format) {

        //Create matcher based on the init pattern for variable names.
        Matcher matcher = variablePattern.matcher(format);

        //This buffer will hold all parts of the formatted finished string.
        StringBuffer formatBuffer = new StringBuffer();

        //loop while the matcher finds another variable (prefix -> name <- suffix) match
        while (matcher.find()) {

            //The root capture group with the full match e.g ${variableName}
            String fullMatch = matcher.group();

            //The capture group for the variable name resulting from "(\w+)" e.g. variableName
            String variableName = matcher.group(1);

            //Get the value in our Map so the Key is the used variable name in our "format" string. The associated value will replace the variable.
            //If key is missing (absent) call the noKeyFunction with parameters "fullMatch" and "variableName" else return the value.
            String value = values.computeIfAbsent(variableName, key -> noKeyFunction.apply(fullMatch, key));

            //Escape the Map value because the "appendReplacement" method interprets the $ and \ as special chars.
            String escapedValue = Matcher.quoteReplacement(value);

            //The "appendReplacement" method replaces the current "full" match (e.g. ${variableName}) with the value from the "values" Map.
            //The replaced part of the "format" string is appended to the StringBuffer "formatBuffer".
            matcher.appendReplacement(formatBuffer, escapedValue);
        }

        //The "appendTail" method appends the last part of the "format" String which has no regex match.
        //That means if e.g. our "format" string has no matches the whole untouched "format" string is appended to the StringBuffer "formatBuffer".
        //Further more the method return the buffer.
        return matcher.appendTail(formatBuffer)
                      .toString();
    }

    public String getPrefix() {
        return prefix;
    }

    public String getSuffix() {
        return suffix;
    }

    public Map<String, String> getValues() {
        return values;
    }
}

Ви можете створити екземпляр класу для конкретної карти зі значеннями (або префіксом суфікса або noKeyFunction), наприклад:

    Map<String, String> values = new HashMap<>();
    values.put("firstName", "Peter");
    values.put("lastName", "Parker");


    FormatHelper formatHelper = new FormatHelper(values);
    formatHelper.format("${firstName} ${lastName} is Spiderman!");
    // Result: "Peter Parker is Spiderman!"
    // Next format:
    formatHelper.format("Does ${firstName} ${lastName} works as photographer?");
    //Result: "Does Peter Parker works as photographer?"

Крім того, ви можете визначити, що станеться, якщо ключ у значеннях Map відсутній (працює обома способами, наприклад, неправильна назва змінної у рядку формату або відсутній ключ у Map). Поведінка за замовчуванням - це викинутий неперевірений виняток (не перевірено, оскільки я використовую функцію jdk8 за замовчуванням, яка не може обробляти перевірені винятки), наприклад:

    Map<String, String> map = new HashMap<>();
    map.put("firstName", "Peter");
    map.put("lastName", "Parker");


    FormatHelper formatHelper = new FormatHelper(map);
    formatHelper.format("${missingName} ${lastName} is Spiderman!");
    //Result: RuntimeException: Key: missingName for variable ${missingName} not found.

Ви можете визначити власну поведінку в виклику конструктора, наприклад:

Map<String, String> values = new HashMap<>();
values.put("firstName", "Peter");
values.put("lastName", "Parker");


FormatHelper formatHelper = new FormatHelper(fullMatch, variableName) -> variableName.equals("missingName") ? "John": "SOMETHING_WRONG", values);
formatHelper.format("${missingName} ${lastName} is Spiderman!");
// Result: "John Parker is Spiderman!"

або делегувати його назад за замовчуванням без ключової поведінки:

...
    FormatHelper formatHelper = new FormatHelper((fullMatch, variableName) ->   variableName.equals("missingName") ? "John" :
            FormatHelper.DEFAULT_NO_KEY_FUNCTION.apply(fullMatch,
                                                       variableName), map);
...

Для кращого керування є також представлення статичних методів, такі як:

Map<String, String> values = new HashMap<>();
values.put("firstName", "Peter");
values.put("lastName", "Parker");

FormatHelper.format("${firstName} ${lastName} is Spiderman!", map);
// Result: "Peter Parker is Spiderman!"

1

На даний момент нічого не вбудовано в Java. Я б запропонував написати власну реалізацію. Мої переваги полягають у простому інтерфейсі вільного побудови, а не в створенні карти та передачі її у функцію - у вас виходить приємний суміжний фрагмент коду, наприклад:

String result = new TemplatedStringBuilder("My name is {{name}} and I from {{town}}")
   .replace("name", "John Doe")
   .replace("town", "Sydney")
   .finish();

Ось проста реалізація:

class TemplatedStringBuilder {

    private final static String TEMPLATE_START_TOKEN = "{{";
    private final static String TEMPLATE_CLOSE_TOKEN = "}}";

    private final String template;
    private final Map<String, String> parameters = new HashMap<>();

    public TemplatedStringBuilder(String template) {
        if (template == null) throw new NullPointerException();
        this.template = template;
    }

    public TemplatedStringBuilder replace(String key, String value){
        parameters.put(key, value);
        return this;
    }

    public String finish(){

        StringBuilder result = new StringBuilder();

        int startIndex = 0;

        while (startIndex < template.length()){

            int openIndex  = template.indexOf(TEMPLATE_START_TOKEN, startIndex);

            if (openIndex < 0){
                result.append(template.substring(startIndex));
                break;
            }

            int closeIndex = template.indexOf(TEMPLATE_CLOSE_TOKEN, openIndex);

            if(closeIndex < 0){
                result.append(template.substring(startIndex));
                break;
            }

            String key = template.substring(openIndex + TEMPLATE_START_TOKEN.length(), closeIndex);

            if (!parameters.containsKey(key)) throw new RuntimeException("missing value for key: " + key);

            result.append(template.substring(startIndex, openIndex));
            result.append(parameters.get(key));

            startIndex = closeIndex + TEMPLATE_CLOSE_TOKEN.length();
        }

        return result.toString();
    }
}

0

Спробуйте Freemarker , бібліотеку шаблонів.

alt текст


4
Фрімаркер? я здогадуюсь, що він готовий знати, як це зробити у звичайній яві. У будь-якому випадку, якщо Freemarker є ймовірною відповіддю, то чи можу я сказати, що JSP теж буде правильною відповіддю?
Ракеш Джуял

1
Дякую, але для мого завдання, схоже, це є певною мірою зайвою. Але спасибі
Енді

1
@ Rakesh JSP - це дуже конкретна річ. Раніше я використовував FreeMarker для створення XML, а іноді навіть генерував файли JAVA. Енді боюся, що вам доведеться написати одну утиліту самостійно (або як та, яка була зазначена вище)
Каннан Еканат

@Boris, хто з них кращий фрімаркер проти швидкості проти рядкового шаблону?
gaurav


0

https://dzone.com/articles/java-string-format-examples String.format (inputString, [listOfParams]) був би найпростішим способом. Заставники в рядку можна визначити наказом. Для отримання більш детальної інформації перегляньте надане посилання.


0

Ви повинні ознайомитися з офіційною бібліотекою ICU4J . Він надає клас MessageFormat, подібний до класу JDK, але цей попередній підтримує названі заповнювачі.

На відміну від інших рішень на цій сторінці. ICU4j є частиною проекту ICU, який підтримується IBM і регулярно оновлюється. Крім того, він підтримує випадки розширеного використання, такі як плюралізація та багато іншого.

Ось приклад коду:

MessageFormat messageFormat =
        new MessageFormat("Publication written by {author}.");

Map<String, String> args = Map.of("author", "John Doe");

System.out.println(messageFormat.format(args));

0

Існує Java-плагін для використання рядкової інтерполяції в Java (наприклад, у Kotlin, JavaScript). Підтримка Java 8, 9, 10, 11 ... https://github.com/antkorwin/better-strings

Використання змінних у рядкових літералах:

int a = 3;
int b = 4;
System.out.println("${a} + ${b} = ${a+b}");

Використання виразів:

int a = 3;
int b = 4;
System.out.println("pow = ${a * a}");
System.out.println("flag = ${a > b ? true : false}");

Використання функцій:

@Test
void functionCall() {
    System.out.println("fact(5) = ${factorial(5)}");
}

long factorial(int n) {
    long fact = 1;
    for (int i = 2; i <= n; i++) {
        fact = fact * i;
    }
    return fact;
}

Для отримання додаткової інформації, будь ласка, прочитайте проект README.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.