Java 一步一步实现高逼格的字符串替换工具(二)

上一篇实现了一个用于字符串替换的方法,主要是利用 正则 + jdk的字符串替换,本篇则会再之前的基础上走一个扩展

1. 之前的方法存在的问题

先把上一篇的两个方法贴下,研究下有什么问题,然后再看下可以怎么去改进

// 获取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 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 batchFormat(String text, List> params) {

List 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 result = new ArrayList<>(params.size());

String[] tempParamAry = new String[keys.size()];

for (Map 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;

}

一个单个替换,一个批量替换,我们一个一个分析,首先看

1. public static String format(String text, Map params)

正则替换效率问题

String.replaceAll() 这个也是走的正则替换, 从我们的业务场景来看,有更好的替换

apache的 commons-lang 有个 StringUtils 工具类, 我们可以用里面的 replace 方法进行代替, 上面注释的就是我们推荐的使用方式

2. public static List batchFormat(String text, List> params)

这个的实现原理比较简单

先用正则把所有需要替换的捞出来, 放在列表中, 并将坑位用数字来替换

然后使用 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对象的实现中,单引号内部有特殊用途,认为两个单引号之间的为一个整体,不做替换

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!

2. 改进++

对上面的正则获取key,然后再调用 MessageFormat.format()的方式不满意,特别是后者的潜规则还不少,我们要实现一个纯粹的,高效的,可扩展的替换工具,应该这么玩?

既然已经深入了MessageFormat的源码,那么就简单了,把他的实现逻辑抠出来,砍掉各种潜规则,我们自己来实现即可

新版的设计思路:

- 首先将文本进行拆分

- 以`{}`作为分割, 大括号前后的各自作为新的`Word`; 大括号内的也作为独立的`Word`

- 将拆分的`Word` 塞入一个数组中

- 遍历上面的数组,替换变量

- 返回想要的结果

实现如下:

public static String formatV2(String text, Map 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 batchFormatV2(String text, List> paramsList) {

List textList = splitText2words(text);

List result = new ArrayList<>();

StringBuilder stringBuilder;

for (Map 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 params) {

if (word.getIsReplaceKey()) {

return params.get(word.getWord()) + "";

} else {

return word.getWord();

}

}

/**

* 将文本根据{}进行分割

*

* 如: {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 splitText2words(String text) {

List 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替换的玩法上

java 批量替换字符串_# Java 一步一步实现高逼格的字符串替换工具(二)相关推荐

  1. java privatekey输出字符串_[Java教程]根据字符串(String)生成公钥(PublicKey)和私钥(PrivateKey)对象_星空网...

    根据字符串(String)生成公钥(PublicKey)和私钥(PrivateKey)对象 2012-05-29 0 1.字符串生成公钥对象 PublicKey /** * 实例化公钥 * * @re ...

  2. java 基础面试 英文_[Java面试] 面试java基础总结大全

    原标题:[Java面试] 面试java基础总结大全 基础知识: 1.JVM.JRE和JDK的区别: JVM(Java Virtual Machine):java虚拟机,用于保证java的跨平台的特性. ...

  3. java查看日志命令_[Java教程]【Linux】linux查看日志文件内容命令tail、cat、tac、head、echo...

    [Java教程][Linux]linux查看日志文件内容命令tail.cat.tac.head.echo 0 2017-11-14 12:00:29 linux查看日志文件内容命令tail.cat.t ...

  4. java 内存泄露 书籍_[Java教程]一次艰难的内存泄露排查,BeanUtils 的锅

    [Java教程]一次艰难的内存泄露排查,BeanUtils 的锅 0 2020-10-29 18:24:42 现象 通过jstat -gcutil pid 5000 ,发现fgc次数很多而且频繁,此时 ...

  5. java实现计算器框架_[Java小程序] 实现简单计算器

    这学期没事学了一点点Java,想写个程序练手,因为只学了一点点,所以暂时只能先写个实现简单功能的计算器练练.感觉写完后不是很好,如果路过的哪位高手给点建议,小弟万分感激啊. 由于期末来了,没太多时间, ...

  6. java视、频_[java视频]感人故 事视 频网 站上那找~~~

    感人故 事视 频网 站上那找~~~ 问题补充:感人故 事视 频网 站上那找~~~ ●呵呵,你问对人了,感人故事就去"新浪show",现在是新浪期下的网站,运行稳定,观看流畅,上传简 ...

  7. java 历遍 类_[Java] 遍历指定包名下所有的类(支持jar) | 学步园

    项目需要,仅做记录. 支持包名下的子包名遍历,并使用Annotation(内注)来过滤一些不必要的内部类,提高命中精度. 通过Thread.currentThread().getContextClas ...

  8. java语言金山打字_[Java教程]java实现 swing模仿金山打字 案例源码

    [Java教程]java实现 swing模仿金山打字 案例源码 0 2014-11-17 12:00:21 java实现 swing模仿金山打字 案例源码,更多Java技术就去Java教程网.http ...

  9. java得到相对路径_[Java]JAVA获取相对路径问题的解决

    1.基本概念的理解 绝对路径:绝对路径就是你的主页上的文件或目录在硬盘上真正的路径,(URL和物理路径)例如: C:xyz est.txt 代表了test.txt文件的绝对路径.http://www. ...

最新文章

  1. 乐山师范计算机科学与技术怎么样,乐山师范学院计算机科学与技术(本科)教育概况...
  2. 关于 Visual Studio 2010
  3. jquery问题,如何调用带this的函数?
  4. ipc620中文版最新版本_(一)Windows10 家庭中文版Docker安装 搭建docker开发环境
  5. Caffe自己修改训练方法
  6. Oracle数据库基本概念理解(3)
  7. jquery学习之-查找父元素方法parent() parents() closest()的区别
  8. 上手深度学习之前,我们先聊聊“数学”
  9. 华为NP课程笔记24-BFD
  10. 短时傅里叶变换原理解
  11. itunes不能读取iPhone的内容,请前往iPhone“偏好设置”的“摘要”选项卡,然后单击“恢复”
  12. 使用纯JavaScript实现全网页动态樱花飘落特效
  13. qq传输文件为什么服务器忙,qq传送离线 接收文件很慢怎么回事
  14. 网站如何做域名转移?闲置域名要及时处理
  15. <C++>初识多态,剖析virtual关键字
  16. 关于 intell IDEA 的代码自动提示功能没有,删了导包也不报红 (已解决)
  17. LINGO编程(基础)
  18. python中的下划线_讲解
  19. 【学习笔记】群论基础
  20. 对于电子签名、CA、证书的理解

热门文章

  1. 迁移python虚拟环境搭建_python虚拟环境virtualenv创建与迁移
  2. Paging Structures in the Different Paging Modes
  3. Linux之hugepage大页内存理论
  4. 我的世界服务器自定义怪物怎么用,我的世界怪物属性自定义教程 怪物属性代码一览...
  5. c查看变量类型_Python入门对象与变量
  6. javascript调用一个函数(对象),new和直接调用的区别
  7. Django如何使用多个数据库
  8. java垃圾回收GC(学习笔记)
  9. 30秒明白tcp的3次握手
  10. 次表面散射材质_游戏开发者怎么做出以假乱真的画面效果?大气散射渲染了解一下...