2012-06-29 21 views
9

是否存在像String.format之類的標準或至少廣泛的實現,但帶有命名參數?具有命名參數的Java字符串templatizer/formatter

我想在這樣的方式來格式化模板化的字符串:

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" 

技術上講,它幾乎一樣:

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

但命名的參數。

我所知道的:

,但它們都使用命令或至少編號的參數,未命名的。我知道實現它是微不足道的,但是在標準Java庫或至少在Apache Commons/Guava /類似的東西中,是否存在一種機制,而沒有引入高調的模板引擎?

注意:我沒有完全成熟的模板引擎很感興趣,有像一些勢在必行/功能邏輯功能,流量控制,改性劑,分模板/夾雜物,迭代器等。通常,下面的方法是工作4行執行 - 這就是我所需要的:

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

任何你不僅僅使用「#」+ args.get(「PATH」)+「/ ls#」+ args.get(「file」)的理由? – Charles

+0

我有一堆模板文件,我有一個參數圖,我需要從每個這些模板文件中獲取填充字符串。 – GreyCat

回答

8

您也可以嘗試org.apache.commons.lang3.text.StrSubstitutor如果Java 7的是不是一種選擇。它的確如此你想要它做什麼。是否輕便可能取決於您是否使用其他公用語言。

+0

謝謝!看起來這正是我所尋找的:) – GreyCat

1

StringTemplate可能是因爲重量輕,你很可能得到一個插值引擎,雖然我不知道它如何資源明智針對諸如FreeMarker,MustacheVelocity的事物。

另一種選擇可能是像MVEL這樣的EL引擎,其具有templating engine

+1

Ahem ...「屬性引用」,「模板引用(如#include或者宏擴展)」,「條件包含子模板」,「模板應用到屬性列表」...... 221K壓縮jar類,124類內部類 - 這絕對不是真正的輕量級。不過,我會檢查其他人,謝謝! – GreyCat

+0

是的,我已經證實所有的FreeMarker,鬍子和速度比我需要的重量要重得多。無論如何,感謝您的建議! – GreyCat

3

我最近發現JUEL很適合描述很好。它是從JSP中取出的表達式語言。它聲稱速度也非常快。

我即將在我自己的一個項目中嘗試一下。

但對於更輕的重量,這是你的一個變種,試試這個(包裹在一個單元測試):

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

我想擴展它來方便的方法添加到快速鍵值對

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

,它不應該太難從Java 7+轉換成Java 6-

+0

我想提一下,它也不是很輕便:它基本上允許執行Java子集,插入一些字符串內。它有138K的壓縮班級,裏面有114班。 – GreyCat

+0

是的,我回答說,從我的手機,我要添加另一個實施,只要我到鍵盤。 –

+0

現在添加了我自己的實現。 –

0

這裏是我的解決方案:

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

我知道我的答案有點晚,但如果你仍然需要這個功能,而不需要下載一個完整的模板引擎,你可以看看aleph-formatter(我是其中一位作者):

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

或者你可以鏈中的參數:

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

// Output: "5 + 10 = 15" 

內部,它的工作原理使用StringBuilder的創建結果通過「解析」的表達,沒有字符串連接,正則表達式/進行更換。

相關問題