若是你有一段模板, 須要用某些數據替換其中的關鍵信息,怎麼作java
"hello, {user}, welcome to {place}!"
經過傳不一樣的user, 和 place 來輸出不一樣的文案數組
##1.通常作法併發
用String.replaceAll 來進行替換就行了, 無非是多調用幾遍,代碼寫起來也簡單,以下工具
@Test public void testReplace() { String text = "hello {user}, welcome to {place}!"; String user = "Lucy"; String place = "China"; String res = text.replace("{user}", user).replace("{place}", place); System.out.println(res); // 輸出 hello Lucy, welcome to China! }
上面看着也沒什麼問題,實現起來也不難,實際呢 ? 若是我想要一個通用的替換方法, 以下面的接口定義, 約定text中用大括號包起來的由後面的參數進行替換性能
要求實現下面這個接口,text爲須要被替換的字符串, 用後面的參數來替換text中用 {}
包含起來的內容code
public String replace(String text, String ... args);
這時,該怎麼用上面的方法來實現替換呢 ?orm
若是有了解過 MessageFormat
的同窗,估計很快就能想到,這個工具就是jdk提供給咱們來實現文本格式化的利器,那麼簡單的實現以下接口
public String replace(String text, Object ... args) { return MessageFormat.format(text, args); } @Test public void testReplace2() { String text = "hello {0}, welcome to {1}!"; String user = "Lucy"; String place = "China"; String ans = replace(text, user, place); System.out.println(ans); // 輸出 hello Lucy, welcome to China! }
仔細瞅瞅,實現了咱們的部分需求,可是還不完美,上面的實現要求{}
中的是後面參數再參數列表中的下標,而咱們但願直接在 {}
中填寫參數名, 直接用後面的參數名來替換, 這個時候能夠怎麼處理 ?字符串
要實現也簡單,我本身先用正則把你的參數撈出來,而後替換成下標數字就能夠了,麻煩的無非是如何寫正則, 如何獲取參數名罷了,正則還好講,參數名的話若是不想用反射,那麼直接改造下 傳參的方式便可,丟一個map
進去就完美了get
// 獲取patter的過程較爲負責,這裏初始化時,作一次便可 private static Pattern pattern; static { pattern = Pattern.compile("((?<=\\{)([a-zA-Z_]{1,})(?=\\}))"); } public String replaceV2(String text, Map<String, Object> map) { List<String> keys = new ArrayList<>(); // 把文本中的全部須要替換的變量撈出來, 丟進keys Matcher matcher = pattern.matcher(text); while (matcher.find()) { String key = matcher.group(); if (!keys.contains(key)) { keys.add(key); } } // 開始替換, 將變量替換成數字, 並從map中將對應的值丟入 params 數組 Object[] params = new Object[keys.size()]; for (int i = 0; i < keys.size(); i++) { text = text.replaceAll(keys.get(i), i + ""); params[i] = map.get(keys.get(i)); } return replace(text, params); } @Test public void testReplaceV2() { String text = "hello {user}, welcome to {place}! {place} is very beautiful "; Map<String, Object> map = new HashMap<>(2); map.put("user", "Lucy"); map.put("place", "China"); String res = replaceV2(text, map); System.out.println(res); // hello Lucy, welcome to China! China is very beautiful }
這下是否是就完美的實現你的需求?
上面的實現,功能是知足了,可是又是正則,又是替換,又是 調用MessageFormat.format
, 這麼多步驟,這不是我想要的結果,幹嗎不直接再 MessageFormat.format
中就把功能實現了,做爲一個有追求的人,怎麼能容忍這種曲線救國!!!(講道理,我是個徹底沒追求的人)
先捋一把MessageFormat
的實現源碼,而後發現上面有個坑,當被替換的是Long型數據時,輸出有點鬼畜
@Test public void testReplaceV2() { String text = "hello {user}, welcome to {place}! now timestamp is: {time} !"; Map<String, Object> map = new HashMap<>(2); map.put("user", "Lucy"); map.put("place", "China"); map.put("time", System.currentTimeMillis()); String res = replaceV2(text, map); System.out.println(res); // 輸出 : hello Lucy, welcome to China! now 2stamp is: 1,490,619,291,742 ! }
根本緣由替換時, 對數字進行了格式化,沒三個加一個,解決方法也比較簡單,不傳數字就能夠了(就是這麼粗暴)
更新後的代碼
public String replaceV2(String text, Map<String, Object> map) { List<String> keys = new ArrayList<>(); // 把文本中的全部須要替換的變量撈出來, 丟進keys Matcher matcher = pattern.matcher(text); while (matcher.find()) { String key = matcher.group(); if (!keys.contains(key)) { keys.add(key); } } // 開始替換, 將變量替換成數字, 並從map中將對應的值丟入 params 數組 Object[] params = new Object[keys.size()]; for (int i = 0; i < keys.size(); i++) { text = text.replaceAll(keys.get(i), i + ""); params[i] = map.get(keys.get(i) + ""); } return replace(text, params); }
若是你硬是要扣細節,要實現第二節裏面定義的格式,不想傳map,這個時候能夠怎麼玩?
--- 我也不知道怎麼玩... 用反射後去的參數名是定義的參數名,若是你的接口定義的是可變參數,實際使用的時候就是一個數組了,這個時候想獲取實際傳入的參數名就無能爲力了
並不完美,在正則獲取結果以後,直接替換結果就行了,幹嗎還要重複屢次一舉!!!,下面這樣不也能夠實現要求麼
public String replaceV3(String text, Map<String, Object> map) { Matcher matcher = pattern.matcher(text); while (matcher.find()) { String key = matcher.group(); text = text.replaceAll("\\{" + key + "\\}", map.get(key) + ""); } return text; } @Test public void testReplaceV3() { String text = "hello {user}, welcome to {place}! {place} is a beautiful place!"; Map<String, Object> map = new HashMap<>(2); map.put("user", "Lucy"); map.put("place", "China"); String res = replaceV3(text, map); System.out.println(res); // hello Lucy, welcome to China! China is a beautiful place! }
上面這種看起來比起前面的正則,撈出來後又去調用 MessageFormat.format
要好多了, 可是也有點問題
對於批量替換,顯然採用前面的方案實現起來簡單且高效多了, 簡單的改造下便可
public List<String> replaceV4(String text, List<Map<String, Object>> mapList) { List<String> keys = new ArrayList<>(); // 把文本中的全部須要替換的變量撈出來, 丟進keys Matcher matcher = pattern.matcher(text); int index = 0; while (matcher.find()) { String key = matcher.group(); if (!keys.contains(key)) { keys.add(key); // 開始替換, 將變量替換成數字, text = text.replaceAll(keys.get(index), index + ""); index ++; } } List<String> result = new ArrayList<>(); // 從map中將對應的值丟入 params 數組 Object[] params = new Object[keys.size()]; for (Map<String, Object> map: mapList) { for (int i = 0; i < keys.size(); i++) { params[i] = map.get(keys.get(i) + ""); } result.add(replace(text, params)); } return result; }
對於上面的實現仍是不滿意,要求既高效、還能夠選擇併發替換、還能支持批量
需求會愈來愈高級,想想該怎麼實現上面的需求呢!
詳情靜待下一篇,主要是借鑑 MessageFormat
的實現原理, 想實現這樣的功能固然是本身動手寫纔是真理