Dize biçimlendirmesinde adlandırılmış yer tutucular


175

Python'da, dizeyi biçimlendirirken, yer tutucuları konum yerine konuma göre doldurabilirim, şöyle:

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

Java'da mümkün olup olmadığını merak ediyorum (umarım harici kütüphaneler olmadan)?


MessageFormat'ı genişletebilir ve değişkenlerdeki indekslere eşleme işlevini uygulayabilirsiniz.
vpram86


1
Bazı tarihler: Java %s, ortak uygulama olan C ++ dünyasından geliştiricileri cezbetmeye çalışırken çoğunlukla C / C ++ 'ı bu konuda kopyaladı . en.wikipedia.org/wiki/Printf_format_string#History Ayrıca bazı IDE ve FindBugs uyumsuzluk% s ve% d sayılarını otomatik olarak algılayabilir, ancak yine de adlandırılmış alanları tercih ederim.
Christophe Roussy

Yanıtlar:


143

Jakarta commons lang StrSubstitutor, değerleriniz zaten doğru biçimlendirilmişse, bunu yapmanın hafif bir yoludur.

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)");

Yukarıdaki sonuçlar:

"Sütun # 2'de yanlış bir '1' değeri var"

Maven kullanırken bu bağımlılığı pom.xml'nize ekleyebilirsiniz:

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

2
Anahtarlar bulunmazsa kütüphanenin atmadığını hayal kırıklığına uğrattım, ancak ${arg}yukarıdaki ( %(arg)) özel yerine varsayılan sözdizimini ( ) kullanırsanız , regex istenen etkiyi derlemez.
John Lehmann

2
Anahtar haritada yoksa özel bir VariableResolver ayarlayabilir ve bir İstisna atayabilirsiniz.
Mene

7
Eski iş parçacığı, ancak 3.6 itibariyle, metin paketi müşterek metin lehine kullanımdan kaldırıldı. commons.apache.org/proper/commons-text
Jeff Walker

74

tam olarak değil, ancak bir değere birden çok kez başvurmak için MessageFormat'ı kullanabilirsiniz :

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

Yukarıdaki String.format () ile de yapılabilir, ancak karmaşık ifadeler oluşturmanız gerekiyorsa messageFormat sözdizimi temizleyici buluyorum, ayrıca dizeye koyduğunuz nesnenin türünü önemsemeniz gerekmiyor


neden yapamadığınızdan emin değilsiniz, dizgideki konum önemli değildir, yalnızca argümanlar listesindeki konum, onu yeniden adlandırma sorunu haline getirir. Anahtarların adını biliyorsunuz, yani argümanlar listesindeki bir anahtarın konumuna karar verebilirsiniz. şu andan itibaren değer 0 ve sütun 1 olarak bilinecektir: MessageeFormat.format ("{1} sütununda, değer olarak {0} kullanılması \" {0} \ "değeri birçok soruna neden olabilir", valueMap .get ('değer'), değerMap.get ('sütun'));
giladbu

1
Bir ipucu için teşekkürler, tam olarak istediğimi yapan basit bir işlev yazmama yardımcı oldu (aşağıya koydum).
Andy

1
Aynı sözdizimi çok daha temiz. Sayısal değerlerin biçimlendirilmesi söz konusu olduğunda çok kötü MessageFormat'ın kendine ait bir fikri vardır.
Kees de Kooter

Ve tek tırnaklarla çevrili yer tutucuları görmezden geliyor gibi görünüyor.
Kees de Kooter

MessageFormatbüyük ama nispeten büyük json içeriği için hantal
EliuX

32

Basit adlandırılmış yer tutucu için Apache Common StringSubstitutor'un başka bir örneği .

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.

Çok büyük dosyalar yüklemeyi bekliyorsanız, bu kütüphanenin replaceInhangi tamponun değerlerini değiştirdiğini de buldum : StringBuilder veya TextStringBuilder. Bu yaklaşımla, dosyanın tüm içeriği belleğe yüklenmez.
Edward Corrigall

15

StringTemplate kütüphanesini kullanabilirsiniz , istediğinizi ve daha fazlasını sunar.

import org.antlr.stringtemplate.*;

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

'unexpected char: '''
Karakterle ilgili

11

İçin çok basit durumlarda sadece bir kodlanmış dize yerine orada bir kütüphane gerek kullanabilirsiniz:

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

UYARI : Sadece mümkün olan en basit kodu göstermek istedim. Elbette bunu, güvenlik yorumlarında belirtildiği gibi ciddi üretim kodu için KULLANMAYIN: kaçış, hata işleme ve güvenlik burada bir sorundur. Ama en kötü durumda artık 'iyi' bir lib kullanmanın neden gerekli olduğunu biliyorsunuz :-)


1
Bu basit ve kolay, ama olumsuz değeri bulunamadığı zaman sessizce başarısız olmasıdır. Sadece yer tutucuyu orijinal dizgide bırakır.
kiedysktos

@kiedysktos, bir çek yaparak geliştirebilirsin, ama tam bir şey istiyorsan, bir lib kullan :)
Christophe Roussy

2
Uyarı: Bu teknik, ara ikame sonuçlarını kendi biçimlendirme dizeleri olarak kabul ettiğinden, bu çözüm dizgi saldırılarına karşı savunmasızdır . Herhangi bir doğru çözüm, biçim dizesinden tek bir geçiş yapmalıdır.
200_success

@ 200_success Evet güvenlik hakkında konuşmak için iyi bir nokta, elbette bu kod ciddi üretim kullanımı için değil ...
Christophe Roussy

8

Yardımların için teşekkür ederim! Tüm ipuçlarını kullanarak, tam olarak istediğimi yapmak için rutin yazdım - sözlük kullanarak python benzeri dize biçimlendirme. Java acemi olduğum için, herhangi bir ipucu takdir.

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

Lombo'nun cevabından farklı olarak, bu sonsuz bir döngüde sıkışamaz, çünkü formatPosiçeremez formatKey.
Aaron Dufour

6
Uyarı: Döngü, ara değiştirme sonuçlarını kendi biçimlendirme dizeleri olarak kabul ettiğinden, bu çözüm dize saldırılarına karşı savunmasızdır . Herhangi bir doğru çözüm, biçim dizesinden tek bir geçiş yapmalıdır.
200_success

6

Bu eski bir iş parçacığıdır, ancak yalnızca kayıt için Java 8 stilini de kullanabilirsiniz, örneğin:

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

Kullanımı:

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."));
}

Çıktı şöyle olacaktır:

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.

Bu harika, ama ne yazık ki burada özellikleri ihlal ediyor docs.oracle.com/javase/8/docs/api/java/util/stream/… İlk parametre kimlikse birleştirici işlevi ikinci parametreyi döndürmelidir. Yukarıdaki kişi bunun yerine kimliği geri getirecektir. Ayrıca bu kuralı da ihlal eder: combiner.apply (u, accumulator.apply (kimlik, t)) == accumulator.apply (u, t)
Ali Cheaito

İlginç ... ancak haritayı geçmek için daha güzel bir yol önerirseniz, mümkünse çoğu biçimlendirme kodu gibi şablondan sonra.
Christophe Roussy

4
Uyarı: Çünkü .reduce()kendi biçimi dizeleri olarak davranır orta ikame sonuçları, bu çözüm biçimi dize saldırılarına karşı zayıf . Herhangi bir doğru çözüm, biçim dizesinden tek bir geçiş yapmalıdır.
200_success

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

Misal:

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
Bu sorunun yanıtı olmalı, çünkü buradaki diğer çözümlerin çoğunun savunmasız olduğu biçim dizesi saldırılarını önler. Java 9'un .replaceAll()dize değiştirme geri çağrıları desteğiyle çok daha basit hale getirdiğini unutmayın .
200_success

Bunun cevabı olmalı, çünkü herhangi bir harici kütüphane kullanmıyor.
Bohao LI

3

Ben yazarı değilim küçük bir kitaplık tam olarak ne istediğinizi yapar:

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

Veya argümanları zincirleyebilirsiniz:

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

// Output: "5 + 10 = 15"

ile koşul tabanlı biçimlendirme yapmak mümkün mü?
gaurav

@gaurav tam olarak değil. Eğer ihtiyacınız varsa tam özellikli bir şablon kütüphaneye ihtiyacınız var.
Andrei Ciobanu

2

Apache Commons Lang'ın replaceEach yöntemi, özel ihtiyaçlarınıza bağlı olarak kullanışlı olabilir. Bu tek yöntem çağrısıyla yer tutucuları adlarıyla değiştirmek için kolayca kullanabilirsiniz:

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

Bazı giriş metinleri verildiğinde, bu, ilk dize dizisindeki yer tutucuların tüm oluşumlarını ikincisinde karşılık gelen değerlerle değiştirir.


1

Bir dize yardımcı sınıfında böyle bir şey olabilir

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

Çok teşekkürler, neredeyse istediğimi yapıyor, ama tek şey değiştiricileri hesaplamıyor ("% (key) 08d" düşünün)
Andy

1
Ayrıca, kullanılan değerlerden herhangi biri ilgili girişi içeriyorsa bunun sonsuz bir döngüye girdiğini unutmayın.
Aaron Dufour

1
Uyarı: Döngü, ara değiştirme sonuçlarını kendi biçimlendirme dizeleri olarak kabul ettiğinden, bu çözüm dize saldırılarına karşı savunmasızdır . Herhangi bir doğru çözüm, biçim dizesinden tek bir geçiş yapmalıdır.
200_success

1

Cevabım:

a) mümkünse StringBuilder kullanın

b) "yer tutucu" konumunu (herhangi bir biçimde: tamsayı en iyisidir, dolar makrosu gibi özel karakterdir) StringBuilder.insert() .

Harici kütüphaneleri kullanmak aşırıya kaçmış gibi görünüyor ve StringBuilder dahili olarak String'e dönüştürüldüğünde performans düşüşü önemli olduğuna inanıyorum.


1

Dayanarak cevap benim yarattığım MapBuildersınıfa:

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

}

sonra StringFormatDize biçimlendirme için sınıf oluşturdum :

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

}

hangi gibi kullanabilirsiniz:

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

1
Uyarı: Döngü, ara değiştirme sonuçlarını kendi biçimlendirme dizeleri olarak kabul ettiğinden, bu çözüm dize saldırılarına karşı savunmasızdır . Herhangi bir doğru çözüm, biçim dizesinden tek bir geçiş yapmalıdır.
200_success

1

Ben de bir dize biçimlendirebilir bir değişkenler oluşumları yerine bir util / helper sınıfı (jdk 8 kullanarak) oluşturdu.

Bu amaçla tüm değiştirmeyi yapan ve yalnızca bir biçim dizesinin etkilenen bölümleri üzerinde döngüler yapan Matchers "appendReplacement" yöntemini kullandım.

Yardımcı sınıf şu anda belgelenmiş değil. Bunu gelecekte değiştireceğim;) Her neyse en önemli satırları yorumladım (umarım).

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

Değerleri (veya sonek öneki veya noKeyFunction) içeren belirli bir Harita için aşağıdaki gibi bir sınıf örneği oluşturabilirsiniz:

    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?"

Ayrıca, Harita değerlerinde bir anahtar eksikse ne olacağını tanımlayabilirsiniz (her iki şekilde de çalışır; örneğin, biçim dizesindeki yanlış değişken adı veya Harita'da eksik anahtar). Varsayılan davranış atılan denetlenmeyen bir istisnadır (işaretli özel durumları işleyemeyen varsayılan jdk8 işlevini kullandığım için işaretlenmemiş):

    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.

Yapıcı çağrısında aşağıdaki gibi özel bir davranış tanımlayabilirsiniz:

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!"

veya varsayılan anahtar olmayan davranışa geri atayın:

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

Daha iyi kullanım için aşağıdakiler gibi statik yöntem gösterimleri de vardır:

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

Bunu yazarken Java'da yerleşik bir şey yok. Kendi uygulamanızı yazmanızı öneririm. Benim tercihim, bir harita oluşturmak ve fonksiyona aktarmak yerine basit bir akıcı oluşturucu arayüzü için - sonuç olarak hoş bir bitişik kod yığını ile sonuçlanırsınız:

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

İşte basit bir uygulama:

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 , şablon kütüphanesini deneyin .

alternatif metin


4
Freemarker? Sanırım, bunu düz java'da nasıl yapacağını bilmek istiyor. Yine de Freemarker olası cevap ise JSP'nin de doğru cevap olacağını söyleyebilir miyim?
Rakesh Juyal

1
Teşekkürler, ama eldeki görevim için bu biraz aşırı gibi görünüyor. Ama teşekkürler.
Andy

1
@Rakesh JSP çok "görünüm / FE" özel bir şeydir. Geçmişte XML oluşturmak ve hatta bazen JAVA dosyaları oluşturmak için FreeMarker kullandım. Andy, kendiniz bir yardımcı program yazmak zorunda kalacağınızdan korkuyorum (veya yukarıda belirtilen gibi)
Kannan Ekanath

@ Hangisi daha iyi freemarker vs hız vs stringtemplate?
gaurav



0

Resmi ICU4J kütüphanesine bir göz atmalısınız . Bir MessageFormat sağlarJDK ile kullanılabilene benzer sınıfı , ancak bu eski adlandırılmış yer tutucuları destekler.

Bu sayfada sunulan diğer çözümlerin aksine. ICU4j, ICU projesinin bir parçasıdır IBM tarafından sürdürülen ve düzenli olarak güncellenen . Buna ek olarak, çoğullaştırma ve çok daha fazlası gibi gelişmiş kullanım durumlarını destekler.

İşte bir kod örneği:

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'da dize enterpolasyonu kullanmak için Java Eklentisi vardır (Kotlin, JavaScript gibi). Destekler Java 8, 9, 10, 11 ... https://github.com/antkorwin/better-strings

Dize değişmezlerinde değişkenler kullanma:

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

İfadeleri kullanma:

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

Fonksiyonları kullanma:

@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;
}

Daha fazla bilgi için lütfen README projesini okuyun.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.