文档章节

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

小灰灰Blog
 小灰灰Blog
发布于 2017/03/28 20:08
字数 1286
阅读 1312
收藏 2

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

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

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

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

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

2. public static List<String> batchFormat(String text, List<Map<String, Object>> 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<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替换的玩法上

© 著作权归作者所有

小灰灰Blog
粉丝 203
博文 212
码字总数 382524
作品 0
武汉
程序员
私信 提问
jvm虚拟机androidy移植-编译篇

有这个必要吗?都过时的东西了,android上的Dalvik效率不够高吗,不够逼格吗? 是的但有总东西是不是我们这些码农能决定的,领导和项目需求才是你要关心的,毕竟工作要向领导汇报,项目要去挣...

lonely1986
2015/04/22
93
1
利用MAVEN的profile 实现打包环境的切换

产生问题的背景 由于在项目开发的时候,我们一般都是使用的本地库,数据库连接写的是本地的,如果我们将项目打成war的时候,里面的配置连接写的是我们本地的,当我们直接把war拷贝到服务器上...

数据加载中
2018/09/15
0
0
怎样衡量两个字符串的相似度(编辑距离动态规划求解)

前言 目前计算句子相似性有很多不同的方案,比如基于语义词典的方法、基于相同词汇的方法、基于统计的方法和基于编辑距离的方法。这篇文章先介绍编辑距离的基础。 编辑距离 编辑距离其实就是...

超人汪小建
2018/06/12
0
0
ZCS与CAS(Central Authentication Service)单点登录系统的集成

原文转自:http://opengeek.cn/forum-viewthread-tid-151-fromuid-23.html CAS( Central Authentication Service)是由JA-SIG开发的一套开源的单点登录系统,在教育行业有着非常广泛的应用,...

红双囍
2011/04/16
858
0
JVM字节码与Java代码层调优

jvm字节码指令 我们都知道,Java源代码不会像C/C++那样直接被编译为机器码,而是被编译成字节码,这造就了Java可以跨平台的特性。JVM实际执行的也是编译后的字节码,所以想要在Java代码层进行...

ZeroOne01
2018/07/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Giraph源码分析(八)—— 统计每个SuperStep中参与计算的顶点数目

作者|白松 目的:科研中,需要分析在每次迭代过程中参与计算的顶点数目,来进一步优化系统。比如,在SSSP的compute()方法最后一行,都会把当前顶点voteToHalt,即变为InActive状态。所以每次...

数澜科技
今天
4
0
Xss过滤器(Java)

问题 最近旧的系统,遇到Xss安全问题。这个系统采用用的是spring mvc的maven工程。 解决 maven依赖配置 <properties><easapi.version>2.2.0.0</easapi.version></properties><dependenci......

亚林瓜子
今天
10
0
Navicat 快捷键

操作 结果 ctrl+q 打开查询窗口 ctrl+/ 注释sql语句 ctrl+shift +/ 解除注释 ctrl+r 运行查询窗口的sql语句 ctrl+shift+r 只运行选中的sql语句 F6 打开一个mysql命令行窗口 ctrl+l 删除一行 ...

低至一折起
今天
9
0
Set 和 Map

Set 1:基本概念 类数组对象, 内部元素唯一 let set = new Set([1, 2, 3, 2, 1]); console.log(set); // Set(3){ 1, 2, 3 } [...set]; // [1, 2, 3] 接收数组或迭代器对象 ...

凌兮洛
今天
4
0
PyTorch入门笔记一

张量 引入pytorch,生成一个随机的5x3张量 >>> from __future__ import print_function>>> import torch>>> x = torch.rand(5, 3)>>> print(x)tensor([[0.5555, 0.7301, 0.5655],......

仪山湖
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部