前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 ># Java 一步一步实现高逼格的字符串替换工具(二)

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

作者头像
一灰灰blog
发布2018-02-06 14:42:55
9860
发布2018-02-06 14:42:55
举报
文章被收录于专栏:小灰灰小灰灰

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Java 一步一步实现高逼格的字符串替换工具(二)
    • 1. 之前的方法存在的问题
      • 1. public static String format(String text, Map<String, Object> params)
      • 2. public static List<String> batchFormat(String text, List<Map<String, Object>> params)
    • 2. 改进++
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档