2012-06-29 26 views
9

Có một tiêu chuẩn hoặc ít nhất là phổ biến thực hiện một cái gì đó như String.format, nhưng với các đối số được đặt tên?Java chuỗi templatizer/định dạng với các đối số được đặt tên

Tôi muốn để định dạng một chuỗi templatized theo một cách như thế:

Map<String, Object> args = new HashMap<String, Object>(); 
args.put("PATH", "/usr/bin"); 
args.put("file", "foo"); 
String s = someHypotheticalMethod("#{PATH}/ls #{file}"); 
// "/usr/bin/ls foo" 

Về mặt kỹ thuật, đó là gần như giống nhau như:

String[] args = new String[] { "/usr/bin", "foo" }; 
String s = String.format("%1$s/ls %2$s", args); 
// "/usr/bin/ls foo" 

nhưng với các đối số được đặt tên.

Tôi nhận thức được:

nhưng tất cả trong số họ sử dụng ra lệnh hoặc ít nhất là số đối số, không có tên những người thân. Tôi biết nó là tầm thường để thực hiện một, nhưng có một cơ chế tôi đang tìm kiếm trong các thư viện Java tiêu chuẩn hoặc ít nhất là trong Apache Commons/Guava/một cái gì đó tương tự, mà không mang trong công cụ mẫu cao cấp?

LƯU Ý: Tôi không thực sự quan tâm đến các công cụ mẫu toàn diện, với các tính năng như logic bắt buộc/chức năng, điều khiển luồng, công cụ sửa đổi, mẫu con/tạp chí, trình lặp, v.v. 4 dòng làm việc thực hiện - đó là tất cả tôi cần:

public static String interpolate(String format, Map<String, ? extends Object> args) { 
    String out = format; 
    for (String arg : args.keySet()) { 
     out = Pattern.compile(Pattern.quote("#{" + arg + "}")). 
       matcher(out). 
       replaceAll(args.get(arg).toString()); 
    } 
    return out; 
} 
+0

Bất kỳ lý do nào bạn không chỉ sử dụng "#" + args.get ("PATH") + "/ ls #" + args.get ("tệp")? – Charles

+0

Tôi có một loạt các tệp mẫu, tôi có một bản đồ các đối số và tôi cần phải nhận các chuỗi được điền từ mọi tệp mẫu này. – GreyCat

Trả lời

8

Bạn cũng có thể thử org.apache.commons.lang3.text.StrSubstitutor nếu Java 7 không phải là một lựa chọn. Nó thực hiện chính xác điều bạn muốn làm. Cho dù đó là trọng lượng nhẹ có thể phụ thuộc vào việc bạn sử dụng cái gì khác của commons-lang là tốt.

+0

Cảm ơn! Có vẻ như đó là chính xác những gì tôi đang tìm kiếm :) – GreyCat

1

StringTemplate có thể như nhẹ một động cơ suy như bạn đang có khả năng để có được, mặc dù tôi không biết làm thế nào nó ngăn xếp lên tài nguyên-khôn ngoan chống lại những thứ như FreeMarker, Mustache hoặc Velocity.

Một tùy chọn khác có thể là một công cụ EL như MVEL, có templating engine.

+1

Ahem ... "tham chiếu thuộc tính", "tham chiếu mẫu (như #include hoặc macro mở rộng)", "bao gồm điều kiện của subtemplate", "ứng dụng mẫu vào danh sách các thuộc tính" ... 221K của các lớp jar nén, 124 lớp bên trong - đó chắc chắn không thực sự nhẹ. Tôi sẽ kiểm tra những người khác, mặc dù, cảm ơn! – GreyCat

+0

Vâng, tôi đã xác nhận rằng tất cả FreeMarker, Mustache và Velocity đều nặng hơn nhiều so với nhu cầu của tôi. Dù sao, cảm ơn cho đề xuất của bạn! – GreyCat

3

Gần đây, tôi đã phát hiện JUEL phù hợp với mô tả độc đáo. Nó là ngôn ngữ biểu hiện được lấy ra từ JSP. Nó tuyên bố là rất nhanh, quá.

Tôi sắp thử dùng thử một trong các dự án của riêng mình.

Nhưng đối với một trọng lượng nhẹ hơn, đó là một biến thể của bạn, hãy thử này (bọc trong một thử nghiệm đơn vị):

public class TestInterpolation { 

    public static class NamedFormatter { 
     public final static Pattern pattern = Pattern.compile("#\\{(?<key>.*)}"); 
     public static String format(final String format, Map<String, ? extends Object> kvs) { 
      final StringBuffer buffer = new StringBuffer(); 
      final Matcher match = pattern.matcher(format); 
      while (match.find()) { 
       final String key = match.group("key"); 
       final Object value = kvs.get(key); 
       if (value != null) 
        match.appendReplacement(buffer, value.toString()); 
       else if (kvs.containsKey(key)) 
        match.appendReplacement(buffer, "null"); 
       else 
        match.appendReplacement(buffer, ""); 
      } 
      match.appendTail(buffer); 
      return buffer.toString(); 
     } 
    } 

    @Test 
    public void test() { 
     assertEquals("hello world", NamedFormatter.format("hello #{name}", map("name", "world"))); 
     assertEquals("hello null", NamedFormatter.format("hello #{name}", map("name", null))); 
     assertEquals("hello ", NamedFormatter.format("hello #{name}", new HashMap<String, Object>())); 
    } 

    private Map<String, Object> map(final String key, final Object value) { 
     final Map<String, Object> kvs = new HashMap<>(); 
     kvs.put(key, value); 
     return kvs; 
    } 
} 

tôi muốn mở rộng nó để thêm các phương pháp thuận tiện để cho khóa-giá trị nhanh chóng cặp

format(format, key1, value1) 
format(format, key1, value1, key2, value2) 
format(format, key1, value1, key2, value2, key3, value3) 
... 

Và nó không phải là quá khó để chuyển đổi từ java 7+ để java 6-

+0

Tôi muốn đề cập rằng nó cũng không thực sự nhẹ: nó cơ bản cho phép thực thi tập hợp con Java, nội suy bên trong một số chuỗi. Đó là 138K giá trị của các lớp học nén, 114 lớp bên trong. – GreyCat

+0

Có, tôi đã trả lời rằng từ điện thoại của mình và sẽ bổ sung một triển khai khác ngay khi tôi vào bàn phím. –

+0

đã thêm bản triển khai của riêng tôi ngay bây giờ. –

0

đây là giải pháp của tôi:

public class Template 
{ 

    private Pattern pattern; 
    protected Map<CharSequence, String> tokens; 
    private String template; 

    public Template(String template) 
    { 
     pattern = Pattern.compile("\\$\\{\\w+\\}"); 
     tokens = new HashMap<CharSequence, String>(); 
     this.template = template; 
    } 

    public void clearAllTokens() 
    { 
     tokens.clear(); 
    } 

    public void setToken(String token, String replacement) 
    { 
     if(token == null) 
     { 
      throw new NullPointerException("Token can't be null"); 
     } 

     if(replacement == null) 
     { 
      throw new NullPointerException("Replacement string can't be null"); 
     } 

     tokens.put(token, Matcher.quoteReplacement(replacement)); 
    } 

    public String getText() 
    { 
     final Matcher matcher = pattern.matcher(template); 
     final StringBuffer sb = new StringBuffer(); 

     while(matcher.find()) 
     { 
      final String entry = matcher.group(); 
      final CharSequence key = entry.subSequence(2, entry.length() - 1); 
      if(tokens.containsKey(key)) 
      { 
       matcher.appendReplacement(sb, tokens.get(key)); 
      } 
     } 
     matcher.appendTail(sb); 
     return sb.toString(); 
    } 


    public static void main(String[] args) { 
     Template template = new Template("Hello, ${name}."); 
     template.setToken("name", "Eldar"); 

     System.out.println(template.getText()); 
    } 
} 
0

Tôi biết câu trả lời của tôi đến muộn một chút, nhưng nếu bạn vẫn cần chức năng này, mà không cần phải tải về một mẫu động cơ chính thức bạn có thể có một cái nhìn tại aleph-formatter (Tôi là một trong các tác giả):

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

Hoặc bạn có thể chuỗi các đối số:

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

// Output: "5 + 10 = 15" 

Bên trong nó hoạt động bằng cách sử dụng StringBuilder tạo kết quả bằng cách "phân tích" sự biểu hiện, không nối chuỗi, regex/thay thế được thực hiện.

Các vấn đề liên quan