专栏首页小灰灰# Java 一步一步实现高逼格的字符串替换工具(二)

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

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • SpringBoot系列教程Web篇之开启GZIP数据压缩

    分享一个真实案例,我们的服务部署在海外,国内访问时访问服务时,响应有点夸张;某些返回数据比较大的接口,耗时在 600ms+上,然而我们的服务 rt 却是在 20...

    一灰灰blog
  • 【SpringBoot WEB 系列】AsyncRestTemplate 之异步非阻塞网络请求介绍篇

    AsyncRestTemplate 发起异步网络请求,由 Spring4.0 引入,但是在 5.0 就被表上了删除注解,官方推荐使用基于 React 的 Web...

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

    Java 一步一步实现高逼格的字符串替换工具(一) 如果你有一段模板, 需要用某些数据替换其中的关键信息,怎么做 "hello, {user}, welc...

    一灰灰blog
  • 在线 Python运行工具

    昨天心血来潮,做了一个在线的PHP编辑工具 http://blog.csdn.net/marksinoberg/article/details/53869...

    py3study
  • 容器监控知多少(1)Docker自带子命令与Weave Scope

    本篇已加入《.NET Core on K8S学习实践系列文章索引》,可以点击查看更多容器化技术相关系列文章。本篇会介绍几个目前比较常用且流行的容器监控工具,首先...

    Edison Zhou
  • 中缀表达式转后缀表达式并求值

    shengjk1
  • kubectl 创建 Pod 背后到底发生了什么?

    原文链接:https://github.com/jamiehannaford/what-happens-when-k8s

    我的小碗汤
  • Kubernetes身份认证和授权操作全攻略:K8s 访问控制入门

    随着Kubernetes被广泛使用,成为业界公认的容器编排管理的标准框架,许多开发人员以及管理员对部署、弹性伸缩以及管理容器化应用程序等Kubernetes的关...

    CNCF
  • SpringBoot基础篇配置信息之如何读取配置信息

    首先创建一个SpringBoot项目,这一块就直接省略掉,下面直奔主题,如何获取配置

    一灰灰blog
  • openCv+VS2015环境搭建

    OpenCV官方网站下载:https://opencv.org/releases/ (官网打开速度有点慢也许打不开,可选择网盘下载或GitHub下载)

    DeROy

扫码关注云+社区

领取腾讯云代金券