利用 Java 的 Matcher#appendReplacement 方法配合正则实现模板语法、token填充功能
应用开发时, 有人可能会有机会遇到需要自己制定模板语法的时候。 比如这几个场景:
1、 回复/评论/私聊 系统 的表情、链接 。 这种模块一般都不会采取富文本的形式来保存, 而是自己制定一个模板语法如 [默认表情: 滑稽] 然后在渲染时再渲染成HTML
2、系统消息推送时携带链接, 并不是每个运营人员都会学习HTML
3、自定义类似于 Markdown 的语法
此场景下的需求
输入参数:
这是一个模板字符串: #[百度|https://www.baidu.com] — #[谷歌|https://www.google.com]
输出参数:
这是一个模板字符串: <a href=’https://www.baidu.com’>百度</a> — <a href=’https://www.google.com’>谷歌</a>
如果说模板语法不一定能用到, 那么我想 token填充 应该是不少场景都会用到的, 很简单就能数出几个类似场景:
1、 短信发送, 短信模板内肯定会有几个 token 槽位。 如: 手机尾号为{{phone}} 的 {{user}} 你好, 你的验证码为:{{verifyCode}}
2、前端控件渲染, vue就不用说了吧
3、站内信\站内消息等推送, 和短信一样, 都是会有模板存在的
此场景下的需求
输入模板:
这是一个模板字符串: <a href='{{link}}’>{{text}}</a>
定义 Tokens:
link=’https://www.baidu.com’ text=’链接’
渲染结果:
这是一个模板字符串: <a href=’https://www.baidu.com’>百度</a> — <a href=’https://www.google.com’>谷歌</a>
实现方式:
像这种场景, 那当然就是正则的出场时机了
有的小机灵鬼可能就知道, 可以用 replaceAll() 方法去替换呀! 这点确实是可以的。 但仅限于 token填充 这一个场景,对模板语法无能为力。
并且也会有性能上的问题, 因为你有多少个token, 就会有多少次替换操作。
这时候就可以轮到 Matcher#appendReplacement
方法上场了:
使用 appendReplacement(StringBuffer sb, String replacement)
方法来可以实现渐进式的替换,就是根据正则进行匹配来一个个的替换, 然后截取替换完毕的内容追加到一个字符串里。
当执行所有的替换操作完成后,最后调用 public StringBuffer appendTail(StringBuffer sb)
方法,补充添加最后剩余的部分。
这样即可将一个模板字符串内所有的待替换项全部按需替换完毕。
下面是示例:
模板语法:
private static void template() { String template = "这是一个模板字符串: #[百度|https://www.baidu.com] -- #[谷歌|https://www.google.com]"; //中间加了 ? 目的是非贪婪匹配 String patternString = "#\\[(.+?\\|.+?)]"; Matcher matcher = Pattern.compile(patternString).matcher(template); StringBuffer sb = new StringBuffer(); while (matcher.find()) { String[] split = matcher.group(1).split("\\|"); String result = String.format("<a href='%s'>%s</a>", split[1], split[0]); matcher.appendReplacement(sb, result); } matcher.appendTail(sb); System.out.println(sb); //这是一个模板字符串: <a href='https://www.baidu.com'>百度</a> -- <a href='https://www.google.com'>谷歌</a> }
tokens 填充:
private static void tokens() { //带token位置的字符串 String template = "这是一个模板字符串: <a href='{{link}}'>{{text}}</a>"; //定义token Map<String, String> tokens = new HashMap(); tokens.put("link", "https://www.baidu.com"); tokens.put("text", "链接"); //生成匹配模式的正则表达式 String patternString = "\\{\\{(" + tokens.keySet().stream().collect(joining("|")) + ")\\}\\}"; Matcher matcher = Pattern.compile(patternString).matcher(template); StringBuffer sb = new StringBuffer(); while (matcher.find()) { matcher.appendReplacement(sb, tokens.get(matcher.group(1))); } matcher.appendTail(sb); System.out.println(sb); //这是一个模板字符串: <a href='https://www.baidu.com'>链接</a> }