การแทนที่สตริงใน java คล้ายกับเทมเพลตความเร็ว


98

มี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}
Adeel Ansari

2
หากคุณต้องการสิ่งที่ดูเหมือนความเร็วและมีกลิ่นเหมือนความเร็วอาจเป็นความเร็ว? :)
serg

@Droo: มันไม่ใช่คลาส ฉันมีข้อความข้างต้นในตัวแปร "String" และต้องการแทนที่การเกิดขึ้นทั้งหมดของสตริงภายใน $ {} ด้วยค่าในออบเจ็กต์ที่เกี่ยวข้อง ตัวอย่างเช่นแทนที่ $ {user.name} ทั้งหมดด้วยคุณสมบัติชื่อในออบเจ็กต์ "ผู้ใช้"
โจ

@serg: ใช่มันเป็นรหัสความเร็ว และฉันต้องการลบความเร็วออกจากโค้ดของฉัน
โจ

คำตอบ:


146

ใช้StringSubstitutorจาก Apache Commons Text

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

4
Javadoc สำหรับ StrSubstitutor commons.apache.org/lang/api-release/org/apache/commons/lang/…
Paul

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/แทน
Lukuluba

7
StrSubstitutorเลิกใช้เมื่อ 1.3 ใช้StringSubstitutorแทน คลาสนี้จะถูกลบออกใน 2.0 การพึ่งพา Gradle สำหรับการนำเข้าStringSubstitutorคือorg.apache.commons:commons-text:1.4
Yurii Rabeshko

อนุญาตให้มีการทดแทนตามเงื่อนไขหรือไม่
Gaurav

132

ดูที่java.text.MessageFormatคลาส MessageFormat รับชุดของวัตถุจัดรูปแบบจากนั้นแทรกสตริงที่จัดรูปแบบลงในรูปแบบตามตำแหน่งที่เหมาะสม

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

10
ขอบคุณ! ฉันรู้ว่า java ควรมีวิธีการในตัวเพื่อทำสิ่งนี้โดยไม่ต้องใช้เครื่องมือเทมเพลตที่แปลกประหลาดในการทำสิ่งง่ายๆ!
Joseph Rajeev Motha

2
ดูเหมือนว่า String.format จะทำอะไรก็ได้ - stackoverflow.com/questions/2809633/…
Noumenon

6
+1. โปรดทราบว่าformatต้องใช้Object...varargs ด้วยเพื่อให้คุณสามารถใช้ไวยากรณ์ที่สั้นกว่านี้ได้ตามที่ต้องการformat("{0} world {1}", "Hello", "!");
davnicwil

ควรสังเกตว่าMessageFormatสามารถใช้ได้อย่างน่าเชื่อถือสำหรับชื่อที่แสดงข้อความเท่านั้นไม่ใช่สำหรับเอาต์พุตที่การจัดรูปแบบทางเทคนิคมีความสำคัญ ตัวอย่างเช่นจะมีการจัดรูปแบบตามการตั้งค่าสถานที่ซึ่งทำให้ไม่ถูกต้องสำหรับการใช้งานทางเทคนิค
Marnes

25

วิธีที่ฉันชอบคือString.format()เพราะเป็น oneliner และไม่ต้องการไลบรารีของบุคคลที่สาม

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


สิ่งนี้มีประโยชน์น้อยกว่าเมื่อคุณต้องทำอาร์กิวเมนต์เดียวกันซ้ำมากกว่าหนึ่งครั้ง เช่น: 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);. คำตอบอื่น ๆ ที่นี่จำเป็นต้องระบุแต่ละอาร์กิวเมนต์เพียงครั้งเดียว
asherbar

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และส่งผ่านในสตริงรูปแบบและแผนที่ของวัตถุและชื่อที่มีในเครื่อง

ผลลัพธ์ต่อไปนี้คือ:

สุนัขของฉันชื่อ fido และ Jane Doe เป็นเจ้าของเขา

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

ฉันควรพูดถึงว่าฉันไม่รู้ว่า Velocity คืออะไรดังนั้นฉันหวังว่าคำตอบนี้จะเกี่ยวข้อง


นี่คือสิ่งที่ฉันกำลังมองหา ขอขอบคุณที่ให้การดำเนินการ ฉันพยายามแล้วและได้ผลลัพธ์ที่ไม่ถูกต้อง : ง. อย่างไรก็ตามมันช่วยแก้ปัญหาของฉันได้
โจ

2
@ โจดีใจที่ช่วยได้ เป็นข้อแก้ตัวที่ดีสำหรับฉันในที่สุดในการฝึกเขียนโค้ดที่ใช้การสะท้อนใน Java
jjnguy

6

นี่คือโครงร่างของวิธีการทำสิ่งนี้ ควรจะค่อนข้างตรงไปตรงมาที่จะใช้เป็นโค้ดจริง

  1. สร้างแผนที่ของวัตถุทั้งหมดที่จะอ้างอิงในแม่แบบ
  2. ใช้นิพจน์ทั่วไปเพื่อค้นหาการอ้างอิงตัวแปรในเทมเพลตและแทนที่ด้วยค่า (ดูขั้นตอนที่ 3) The Matcherชั้นจะมาในที่มีประโยชน์สำหรับการค้นหาและแทนที่
  3. แยกชื่อตัวแปรที่จุด user.nameจะกลายเป็นและuser nameค้นหาuserในแผนที่ของคุณเพื่อรับวัตถุและใช้การสะท้อนเพื่อรับค่าnameจากวัตถุ สมมติว่าวัตถุของคุณมีตัวรับมาตรฐานคุณจะมองหาวิธีการgetNameและเรียกใช้

เหอเพิ่งเห็นคำตอบนี้ มันเหมือนกับของฉัน โปรดแจ้งให้เราทราบว่าคุณคิดอย่างไรกับการใช้งานของฉัน
jjnguy

5

มีการใช้งาน Expression Language สองสามแบบที่ทำสิ่งนี้ให้คุณได้ดีกว่าที่จะใช้การใช้งานของคุณเองหรือหากข้อกำหนดของคุณเติบโตขึ้นดูตัวอย่างเช่นJUELและMVEL

ฉันชอบและประสบความสำเร็จในการใช้ MVEL ในโครงการอย่างน้อยหนึ่งโครงการ

ดูโพสต์ Stackflow JSTL / JSP EL (Expression Language) ในบริบทที่ไม่ใช่ JSP (สแตนด์อโลน)


0

ไม่มีอะไรนอกกรอบที่เทียบได้กับความเร็วเนื่องจากความเร็วถูกเขียนขึ้นเพื่อแก้ปัญหานั้นอย่างแท้จริง สิ่งที่ใกล้เคียงที่สุดที่คุณสามารถลองได้คือการค้นหาฟอร์แมตเตอร์

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);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.