• replace 替换全部的正确姿势


    本文同步自我的个人博客:http://www.52cik.com/2015/11/06/replace-all.html

    关于字符串替换问题,其实是个很简单的问题,但却也不那么简单,至少对于很多新手而言,全局替换一直是个坑。

    简单而强大的正则

    可能你觉得要替换全局,就改成正则,然后加个 g 全局匹配就好了,例如:

    var str = "test-test-test";
    str = "test-test-test".replace("test", "ok");
    console.log(str);
    

    改成正则:

    var str = "test-test-test";
    str = "test-test-test".replace(/test/g, "ok");
    console.log(str);
    

    确实非常简单,但是如果出现需要转义的字符呢?
    当然也难不倒大家,但是对于新手而言,正则就是一座无形的大山,完全无法越过。

    无奈的正则

    例如表情标签的替换呢?难道全部改成正则?那也要会正则的人花不少体力才能搞定吧。
    今天群里的朋友就遇到这么个问题,表情标签如下:

    var faces = {
      "/::)": "weixiao",
      "/::~": "pizui",
      "/::B": "se",
      "/::|": "fadai",
      "/:8-)": "deyi",
      "/::<":"liulei",
      "/::$": "haixiu",
      "/::'(": "daku",
      "/::-|": "gangga"
    };
    

    当然我只是截取了部分,好像有好几十个,如果全部改成正则,需要不少体力呢。

    循环替换

    这种情况,我们需要正常的字符串替换,例如结合 while + indexOf 实现。

    var faces = {
      "/::)": "weixiao",
      "/::~": "pizui",
      "/::B": "se",
      "/::|": "fadai",
      "/:8-)": "deyi",
      "/::<":"liulei",
      "/::$": "haixiu",
      "/::'(": "daku",
      "/::-|": "gangga"
    };
    
    var str = "/::)-/::B-/::)-/:8-)-/:8-)";
    
    for (var k in faces) {
      while(str.indexOf(k) > -1) {
        str = str.replace(k, faces[k]);
      }
    }
    
    console.log(str);
    

    这样,基本功能实现,不过这是有问题的,如果有一个键值相同的,就会死循环例如:

    var faces = {
      "/::)": "weixiao",
      "/:hehe": "/:hehe"
    };
    
    var str = "/::)-/::B-/:hehe-/:8-)-/:8-)";
    
    for (var k in faces) {
      while(str.indexOf(k) > -1) {
        str = str.replace(k, faces[k]);
      }
    }
    
    console.log(str);
    

    这样,就呵呵了,当然不一定会有这样的情况,但也不能肯定一定没有这样的情况。

    改进循环

    我们需要用到 indexOf 的第二个参数来规避这种情况,改进后的代码如下:

    var faces = {
      "/::)": "weixiao",
      "/:hehe": "/:hehe"
    };
    
    var str = "/::)-/::B-/:hehe-/:8-)-/:8-)";
    
    for (var k in faces) {
      var p = -1; // 字符出现位置
      var s = 0; // 下一次起始位置
      while((p = str.indexOf(k, s)) > -1) {
        s = p + faces[k].length; // 位置 + 值的长度
        str = str.replace(k, faces[k]);
      }
    }
    
    console.log(str);
    

    好了,现在这样就没问题了,也不用担心死循环问题,而且替换大段文字的时候,性能比正则要好。 经过测试,确实正则快。
    在大段正则匹配的时候,回溯会导致匹配性能问题,所以才一直认为正则慢,而这种情况的正则,不需要回溯,性能自然也极佳。

    为了方便新手朋友使用,下面写个函数吧。

    /**
     * 字符串替换
     * @param  {string} str    要被替换的字符串
     * @param  {string} substr 要替换的字符串
     * @param  {string} newstr 用于替换的字符串
     * @return {string}        替换后的新字符串
     */
    function replace(str, substr, newstr) {
      var p = -1; // 字符出现位置
      var s = 0; // 下一次起始位置
    
      while((p = str.indexOf(substr, s)) > -1) {
        s = p + newstr.length; // 位置 + 值的长度
        str = str.replace(substr, newstr);
      }
    
      return str;
    }
    
    console.log( replace("ssssss", "ss", "s") ); // sss
    

    总结

    我发现,我也只能写写这些小东西,唉。。

    后记

    在 4楼 Antineutrino 的测试中得知,正则方法比 while + indexOf 性能更佳。
    之前一直认为正则就是慢的代名词,所以直接忽略了正则方法,现在又重新认识了正则,正则果然犀利。

    抽空写了个正则版本的,把元字符种的几个符号转义下,就可以生成正则了。
    网上那些正则版本中没有转义直接 new RegExp 的会导致各种bug,所以要处理下元字符的那些符号。

    /**
     * 字符串替换
     * @param  {string} str    要被替换的字符串
     * @param  {string} substr 要替换的字符串
     * @param  {string} newstr 用于替换的字符串
     * @return {string}        替换后的新字符串
     */
    function replace(str, substr, newstr) {
      substr = substr.replace(/[.\[]{}()|^$?*+]/g, "\$&"); // 转义字符串中的元字符
      var re = new RegExp(substr, "g"); // 生成正则
      return str.replace(re, newstr);
    }
    
    console.log( replace("ssssss", "ss", "s") ); // sss
    
  • 相关阅读:
    我的黑客偶像
    IEEE754浮点数
    罗马数字转化为阿拉伯数字
    2020-2021-1 20201221 《信息安全专业导论》第三周学习总结
    base64
    2020-2021-1 20201221曾思源《第二周学习总结》
    师生关系
    教材速读问题
    20201221曾思源自我介绍
    20201221 曾思源 第一周学习总结
  • 原文地址:https://www.cnblogs.com/52cik/p/replace-all.html
Copyright © 2020-2023  润新知