上一篇实现了一个用于字符串替换的方法,主要是利用 正则 + jdk的字符串替换,本篇则会再以前的基础上走一个扩展java
先把上一篇的两个方法贴下,研究下有什么问题,而后再看下能够怎么去改进apache
// 获取patter的过程较为负责,这里初始化时,作一次便可 private static Pattern pattern; static { pattern = Pattern.compile("((?<=\\{)([a-zA-Z_]{1,})(?=\\}))"); } /** * 字符串替换, 将 {} 中的内容, 用给定的参数进行替换 * * @param text * @param params * @return */ public static String format(String text, Map<String, Object> params) { // 把文本中的全部须要替换的变量捞出来, 丢进keys Matcher matcher = pattern.matcher(text); while (matcher.find()) { String key = matcher.group(); // text = StringUtils.replace(text, "{" + key + "}", params.get(key) + ""); text = text.replaceAll("\\{" + key + "\\}", params.get(key) + ""); } return text; } public static List<String> batchFormat(String text, List<Map<String, Object>> params) { List<String> keys = new ArrayList<>(); // 把文本中的全部须要替换的变量捞出来, 丢进keys Matcher matcher = pattern.matcher(text); int tempIndex = 0; while (matcher.find()) { String key = matcher.group(); if (keys.contains(key)) { continue; } text = StringUtils.replace(text, key, tempIndex + ""); tempIndex++; keys.add(key); } List<String> result = new ArrayList<>(params.size()); String[] tempParamAry = new String[keys.size()]; for (Map<String, Object> param : params) { for (int i = 0; i < keys.size(); i++) { tempParamAry[i] = param.get(keys.get(i)) + ""; } result.add(MessageFormat.format(text, tempParamAry)); } return result; }
一个单个替换,一个批量替换,咱们一个一个分析,首先看数组
public static String format(String text, Map<String, Object> params)
String.replaceAll()
这个也是走的正则替换, 从咱们的业务场景来看,有更好的替换apache的 commons-lang 有个 StringUtils
工具类, 咱们能够用里面的 replace
方法进行代替, 上面注释的就是咱们推荐的使用方式app
public static List<String> batchFormat(String text, List<Map<String, Object>> params)
这个的实现原理比较简单ide
MessageFormat.format
进行替换这个流程比较清晰简单,对于 MessageFormat.format
却发现一个诡异的问题,当text中包含单引号时,后面的不会被替换, 测试case以下工具
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); text = "hello {0}, welcome to {2} ! what's a good day! today is {1}!"; ans = replace(text, "Lucy", new Date(), "HangZhou"); System.out.println(ans); }
输出以下:测试
debug到源码去看下,而后发如今生成 MessageFormat
对象的实现中,单引号内部有特殊用途,认为两个单引号之间的为一个总体,不作替换ui
String text = "hello {0}, welcome to {2} ! what's {0}' a good day! today is {1}!"; String ans = MessageFormat.format(text, "Lucy", new Date(), "HangZhou"); System.out.println(ans); // 输出 hello Lucy, welcome to HangZhou ! whats {0} a good day! today is 17-3-28 下午5:54!
对上面的正则获取key,而后再调用
MessageFormat.format()
的方式不满意,特别是后者的潜规则还很多,咱们要实现一个纯粹的,高效的,可扩展的替换工具,应该这么玩?this
既然已经深刻了MessageFormat
的源码,那么就简单了,把他的实现逻辑抠出来,砍掉各类潜规则,咱们本身来实现便可debug
新版的设计思路:
- 首先将文本进行拆分 - 以`{}`做为分割, 大括号先后的各自做为新的`Word`; 大括号内的也做为独立的`Word` - 将拆分的`Word` 塞入一个数组中 - 遍历上面的数组,替换变量 - 返回想要的结果
实现以下:
public static String formatV2(String text, Map<String, Object> params) { StringBuilder stringBuilder = new StringBuilder(); int startIndex = 0; for (int i = 0; i < text.length(); i++) { if (text.charAt(i) == '{') { if (startIndex > 0) { stringBuilder.append(text.substring(startIndex, i)); } startIndex = i + 1; continue; } if (text.charAt(i) == '}') { stringBuilder.append(params.get(text.substring(startIndex, i))); startIndex = i + 1; } } if (startIndex < text.length()) { stringBuilder.append(text.substring(startIndex)); } return stringBuilder.toString(); } /** * 规定大括号中不能再次出现大括号, 即不容许迭代替换 * * @param text * @param paramsList * @return */ public static List<String> batchFormatV2(String text, List<Map<String, Object>> paramsList) { List<Word> textList = splitText2words(text); List<String> result = new ArrayList<>(); StringBuilder stringBuilder; for (Map<String, Object> params: paramsList) { stringBuilder = new StringBuilder(); for (Word word: textList) { stringBuilder.append(replaceWord(word, params)); } result.add(stringBuilder.toString()); } return result; } private static String replaceWord(Word word, Map<String, Object> params) { if (word.getIsReplaceKey()) { return params.get(word.getWord()) + ""; } else { return word.getWord(); } } /** * 将文本根据{}进行分割 * <p/> * 如: {place} is a good place, what do you think {user}? * 分割: * - Word("place", true) * - Word(" is a good place, what do you think ", false) * - Word("user", true) * - Word("?", false) * * @param text * @return */ private static List<Word> splitText2words(String text) { List<Word> textList = new ArrayList<>(); int startIndex = 0; for (int i = 0; i < text.length(); i++) { if (text.charAt(i) == '{') { if (startIndex > 0) { textList.add(new Word(text.substring(startIndex, i), false)); } startIndex = i + 1; continue; } if (text.charAt(i) == '}') { textList.add(new Word(text.substring(startIndex, i), true)); startIndex = i + 1; } } if (startIndex < text.length()) { textList.add(new Word(text.substring(startIndex), false)); } return textList; } private static class Word { private String word; /** * true 则表示保存的是须要被替换的值 */ private Boolean isReplaceKey; public Word(String word, Boolean replaceKey) { this.word = word; this.isReplaceKey = replaceKey; } public String getWord() { return word; } public Boolean getIsReplaceKey() { return isReplaceKey; } @Override public String toString() { return "Word{" + "word='" + word + '\'' + ", isReplaceKey=" + isReplaceKey + '}'; } }
至此,一个算是不错的文本替换工具类出来了,想一想还有什么能够改进的地方么?
简单的字符串进行替换有点low,若是我想在 {}
中执行一些表达式能够怎么玩 ?
下一篇则将精力主要集中在 {}
中value替换的玩法上