Thay thế chuỗi trong java, tương tự như mẫu vận tốc


96

Có bất kỳ Stringcơ chế thay thế nào trong Java không, nơi tôi có thể truyền các đối tượng bằng văn bản và nó thay thế chuỗi khi nó xảy ra.
Ví dụ, văn bản là:

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

Các đối tượng tôi có là "user""site". Tôi muốn thay thế các chuỗi được cung cấp bên trong ${}bằng các giá trị tương đương của nó từ các đối tượng. Điều này cũng giống như chúng ta thay thế các đối tượng trong một mẫu vận tốc.


1
Thay ở đâu? Một lớp học? Một JSP? Chuỗi có một phương pháp định dạng nếu bạn chỉ:String.format("Hello %s", username);
Droo

1
@Droo: Trong ví dụ, chuỗi giống Hello ${user.name}, không giống, Hello %shoặc Hello {0}.
Adeel Ansari

2
Nếu bạn cần một cái gì đó giống như vận tốc và có mùi giống như vận tốc, có lẽ nó là vận tốc? :)
serg

@Droo: Nó không phải là một lớp học. Tôi đã viết văn bản ở trên trong một biến "Chuỗi" và muốn thay thế tất cả các lần xuất hiện của các chuỗi bên trong $ {} bằng các giá trị trong các đối tượng tương ứng. ví dụ: thay thế tất cả $ {user.name} bằng thuộc tính name trong đối tượng "user".
Joe

@serg: Vâng, đó là mã vận tốc. và tôi muốn xóa vận tốc khỏi mã của mình.
Joe

Câu trả lời:


142

Sử dụng StringSubstitutortừ Apache Commons Text.

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

Nó sẽ làm điều đó cho bạn (và mã nguồn mở của nó ...)

 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
Nên Map<String, String> valuesMap = new HashMap<String, String>();.
andrewrjones

3
StrSubstitutorhiện không được dùng nữa trong https://commons.apache.org/proper/commons-lang/ . Thay vào đó, người dùng https://commons.apache.org/proper/commons-text/
Lukuluba

7
StrSubstitutorkhông dùng nữa kể từ 1.3, hãy sử dụng StringSubstitutorthay thế. Lớp này sẽ bị xóa trong 2.0. Phụ thuộc Gradle cho nhập khẩu StringSubstitutororg.apache.commons:commons-text:1.4
Yurii Rabeshko

nó có cho phép thay thế dựa trên điều kiện không?
Gaurav

129

Hãy nhìn vào java.text.MessageFormatlớp, MessageFormat lấy một tập hợp các đối tượng, định dạng chúng, sau đó chèn các chuỗi đã định dạng vào mẫu ở những vị trí thích hợp.

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

10
Cảm ơn! Tôi biết java nên có một cách sẵn có để làm điều này mà không cần phải sử dụng công cụ mẫu kỳ lạ để làm một điều đơn giản như vậy!
Joseph Rajeev Motha,

2
Có vẻ như String.format có thể làm bất cứ điều gì mà điều này có thể làm - stackoverflow.com/questions/2809633/…
Noumenon

6
+1. Hãy nhận biết formatcũng có một Object...varargs vì vậy bạn có thể sử dụng cú pháp này ngắn gọn hơn nơi thích hợp hơnformat("{0} world {1}", "Hello", "!");
davnicwil

Cần lưu ý rằng MessageFormatchỉ có thể được sử dụng một cách đáng tin cậy cho tên của nó, hiển thị thông báo, không cho đầu ra khi định dạng kỹ thuật quan trọng. Ví dụ, các con số sẽ được định dạng theo cài đặt ngôn ngữ, khiến chúng không hợp lệ cho các mục đích sử dụng kỹ thuật.
'19

22

Cách ưa thích của tôi là String.format()vì nó là oneliner và không yêu cầu thư viện của bên thứ ba:

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

Tôi sử dụng điều này thường xuyên, ví dụ: trong các thông báo ngoại lệ như:

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

Gợi ý: Bạn có thể đặt bao nhiêu biến tùy thích vì format()sử dụng Varargs


Điều này ít hữu ích hơn khi bạn cần lặp lại cùng một đối số nhiều hơn một lần. Ví dụ: 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);. Các câu trả lời khác ở đây yêu cầu chỉ định mỗi đối số một lần.
asherbar

2
@asherbar, bạn có thể sử dụng công cụ xác định chỉ mục đối số trong chuỗi định dạng, ví dụ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 Tôi chưa bao giờ biết điều đó. Cảm ơn!
asherbar

20

Tôi đã cùng nhau thực hiện một thử nghiệm nhỏ về điều này. Ý tưởng cơ bản là gọi formatvà chuyển vào chuỗi định dạng, bản đồ các đối tượng và tên mà chúng có cục bộ.

Kết quả sau đây là:

Con chó của tôi tên là fido, và Jane Doe sở hữu nó.

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

Lưu ý: Điều này không biên dịch do các ngoại lệ không được xử lý. Nhưng nó làm cho mã dễ đọc hơn nhiều.

Ngoài ra, tôi không thích việc bạn phải tự xây dựng bản đồ trong mã, nhưng tôi không biết cách lấy tên của các biến cục bộ theo chương trình. Cách tốt nhất để làm điều đó, là nhớ đưa đối tượng vào bản đồ ngay khi bạn tạo nó.

Ví dụ sau tạo ra kết quả mà bạn muốn từ ví dụ của mình:

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

Tôi cũng nên đề cập rằng tôi không biết Vận tốc là gì, vì vậy tôi hy vọng câu trả lời này có liên quan.


Đây là những gì tôi đang tìm kiếm. Cảm ơn bạn đã triển khai. Tôi đã cố gắng cho nó và nhận được kết quả không chính xác. : D. Dù sao nó cũng giải quyết được vấn đề của tôi.
Joe

2
@Joe, rất vui vì tôi có thể giúp đỡ. Đó là một lý do tốt để cuối cùng tôi thực hành viết một số mã sử dụng phản xạ trong Java.
jjnguy

6

Đây là một phác thảo về cách bạn có thể làm điều này. Nó sẽ tương đối đơn giản để triển khai nó dưới dạng mã thực tế.

  1. Tạo một bản đồ của tất cả các đối tượng sẽ được tham chiếu trong mẫu.
  2. Sử dụng một biểu thức chính quy để tìm các tham chiếu biến trong mẫu và thay thế chúng bằng các giá trị của chúng (xem bước 3). Lớp Matcher sẽ có ích cho việc tìm và thay thế.
  3. Tách tên biến tại dấu chấm. user.namesẽ trở thành username. Tra cứu usertrong bản đồ của bạn để lấy đối tượng và sử dụng phản xạ để lấy giá trị nametừ đối tượng. Giả sử các đối tượng của bạn có các getters tiêu chuẩn, bạn sẽ tìm kiếm một phương thức getNamevà gọi nó.

Heh, vừa thấy câu trả lời này. Nó giống hệt của tôi. Vui lòng cho tôi biết bạn nghĩ gì về cách triển khai của tôi.
jjnguy

5

Có một số triển khai Ngôn ngữ biểu hiện có sẵn để thực hiện điều này cho bạn, có thể tốt hơn là sử dụng triển khai của riêng bạn khi hoặc nếu các yêu cầu của bạn tăng lên, hãy xem ví dụ như JUELMVEL

Tôi thích và đã sử dụng thành công MVEL trong ít nhất một dự án.

Cũng xem bài đăng trên Stackflow JSTL / JSP EL (Ngôn ngữ biểu thức) trong ngữ cảnh không phải JSP (độc lập)


0

Không có gì có thể so sánh được với vận tốc vì vận tốc được viết ra để giải chính xác vấn đề đó. Điều gần nhất bạn có thể thử là xem xét Định dạng

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

Tuy nhiên trình định dạng theo như tôi biết được tạo ra để cung cấp các tùy chọn định dạng C giống như trong Java nên nó có thể không gây ngứa chính xác cho bạn nhưng bạn có thể thử :).


0

Tôi sử dụng GroovyShell trong java để phân tích cú pháp mẫu với 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);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.