Hız şablonuna benzer şekilde javada dize değişimi


98

StringJava'da nesneleri bir metinle geçirebileceğim herhangi bir değiştirme mekanizması var mı ve oluştuğunda dizeyi değiştirir.
Örneğin, metin:

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

Sahip olduğum nesneler "user"ve "site". İçeride verilen dizgeleri ${}nesnelerden eşdeğer değerleri ile değiştirmek istiyorum . Bu, bir hız şablonundaki nesneleri değiştirmemizle aynıdır.


1
Nerede değiştirilsin? Bir sınıf? Bir JSP? String.format("Hello %s", username);
Aşağıdaki durumlarda, dizenin

1
@Droo: Örnekte, string Hello ${user.name}, Hello %sveya gibi değil, gibidir Hello {0}.
Adeel Ansari

2
Hız gibi görünen ve hız gibi kokan bir şeye ihtiyacınız varsa, bu hız olabilir mi? :)
serg

@Droo: Bu bir sınıf değil. Bir "String" değişkeninde yukarıdaki metne sahibim ve $ {} içindeki dizelerin tüm oluşumlarını karşılık gelen nesnelerdeki değerlerle değiştirmek istiyor. örneğin, tüm $ {user.name} 'yi "user" nesnesindeki name özelliği ile değiştirin.
Joe

@serg: Evet bu bir hız kodudur. ve hızı kodumdan kaldırmak istiyorum.
Joe

Yanıtlar:


146

StringSubstitutorApache Commons Metninden kullanın .

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

Bunu sizin için yapacak (ve açık kaynağı ...)

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


1
Olmalı Map<String, String> valuesMap = new HashMap<String, String>();.
andrewrjones

3
StrSubstitutorartık https://commons.apache.org/proper/commons-lang/ adresinde kullanımdan kaldırılmıştır . Bunun yerine https://commons.apache.org/proper/commons-text/ kullanıcısı
Lukuluba

7
StrSubstitutor1.3 itibariyle kullanımdan kaldırıldı, StringSubstitutorbunun yerine kullanın. Bu sınıf 2.0'da kaldırılacaktır. İthal için Gradle bağımlılık StringSubstitutorolduğunuorg.apache.commons:commons-text:1.4
Yurii Rabeshko

koşula dayalı ikameye izin veriyor mu?
Gaurav

132

java.text.MessageFormatSınıfa bir göz atın , MessageFormat bir dizi nesneyi alır, bunları biçimlendirir, ardından biçimlendirilmiş dizeleri uygun yerlerde kalıba ekler.

Object[] params = new Object[]{"hello", "!"};
String msg = MessageFormat.format("{0} world {1}", params);

10
Teşekkürler! Java'nın bunu kullanmak zorunda kalmadan yapmak için dahili bir yolu olması gerektiğini biliyordum, bu kadar basit bir şey yapmak için korkunç şablon motoru!
Joseph Rajeev Motha

2
Görünüşe göre String.format bunun yapabileceği her şeyi yapabilir - stackoverflow.com/questions/2809633/…
Noumenon

6
+1. formatAyrıca bir Object...değişken aldığının farkında olun , böylece bu daha kısa sözdizimini tercih format("{0} world {1}", "Hello", "!");
edilirse

MessageFormatTeknik biçimlendirmenin önemli olduğu durumlarda çıktı için değil, yalnızca adaşı, ekran mesajları için güvenilir bir şekilde kullanılabileceğine dikkat edilmelidir . Örneğin, sayılar yerel ayarlara göre biçimlendirilecek ve teknik kullanımlar için geçersiz kılacaktır.
Marnes

25

Tercih String.format()ettiğim yol, onun bir oneliner olması ve üçüncü taraf kitaplıkları gerektirmemesi:

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

Bunu düzenli olarak kullanıyorum, örneğin aşağıdaki gibi istisna mesajlarında:

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

İpucu: İstediğiniz kadar değişken girebilirsiniz çünkü Varargsformat() kullanır


Aynı argümanı birden fazla tekrarlamanız gerektiğinde bu daha az yararlıdır. Ör: 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);. Buradaki diğer cevaplar her argümanı yalnızca bir kez belirtmeyi gerektirir.
asherbar

2
@asherbar, biçim dizesinde bağımsız değişken dizini belirticilerini kullanabilirsiniz, örneğinString.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 Bunu hiç bilmiyordum. Teşekkürler!
asherbar

20

Bunun küçük bir test uygulamasını bir araya getirdim. Temel fikir, formatbiçim dizesini ve nesnelerin bir haritasını ve yerel olarak sahip oldukları isimleri çağırmak ve iletmektir.

Aşağıdakilerin çıktısı:

Köpeğimin adı fido ve Jane Doe onun sahibi.

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

Not: Bu, işlenmemiş istisnalar nedeniyle derlenmez. Ancak kodu okumayı çok daha kolay hale getirir.

Ayrıca, haritayı kodda kendiniz oluşturmanızdan hoşlanmıyorum, ancak yerel değişkenlerin adlarını programatik olarak nasıl alacağımı bilmiyorum. Bunu yapmanın en iyi yolu, nesneyi yaratır yaratmaz haritaya koymayı hatırlamaktır.

Aşağıdaki örnek, örneğinizden istediğiniz sonuçları üretir:

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

Ayrıca, Hızın ne olduğu hakkında hiçbir fikrim olmadığını da belirtmeliyim, bu yüzden bu cevabın alakalı olduğunu umuyorum.


Aradığım buydu. Uygulama yaptığınız için teşekkür ederiz. Deniyordum ve yanlış sonuçlar alıyordum. : D. Her neyse sorunumu çözdü.
Joe

2
@Joe, yardımcı olabildiğime sevindim. Sonunda Java'da yansıma kullanan bir kod yazma pratiği yapmak benim için iyi bir bahaneydi.
jjnguy

6

İşte bunu nasıl yapabileceğinizin bir özeti. Bunu gerçek kod olarak uygulamak görece basit olmalıdır.

  1. Şablonda referans verilecek tüm nesnelerin bir haritasını oluşturun.
  2. Şablonda değişken referansları bulmak ve bunları değerleriyle değiştirmek için normal bir ifade kullanın (bkz. 3. adım). Maçlar bulmak ve değiştirme için sınıf kullanışlı olacaktır.
  3. Değişken adını noktadan ayırın. user.nameolur userve name. userNesneyi elde etmek için haritanıza bakın ve nesneden değerini elde etmek için yansımayı kullanın name. Nesnelerinizin standart alıcılara sahip olduğunu varsayarsak, bir yöntem arayacak getNameve onu çağıracaksınız.

Heh, bu cevabı şimdi gördüm. Benimkiyle aynı. Lütfen uygulamam hakkında ne düşündüğünüzü bana bildirin.
jjnguy


0

Hız tam olarak bu sorunu çözmek için yazıldığından, kutunun dışında hız ile karşılaştırılabilecek hiçbir şey yoktur. Deneyebileceğiniz en yakın şey Biçimlendiriciye bakmaktır

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

Ancak, bildiğim kadarıyla biçimlendirici Java'da C benzeri biçimlendirme seçenekleri sağlamak için yaratıldı, bu yüzden tam olarak kaşıntınızı çizmeyebilir, ancak denemeye davetlisiniz :).


0

Groovy GString ile şablonu ayrıştırmak için Java'da GroovyShell kullanıyorum:

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