【问题标题】:Fastest method to replace all instances of a character in a string [duplicate]替换字符串中字符的所有实例的最快方法[重复]
【发布时间】:2011-01-08 03:54:14
【问题描述】:

在 JavaScript 中替换字符串中字符串/字符的所有实例的最快方法是什么?一个while,一个for-loop,一个正则表达式?

【问题讨论】:

  • while 和 for 循环都将使用简单的算法在 O(n) 中运行。不确定在这种情况下 Javascript 正则表达式引擎的时间复杂度是多少,但我的猜测是它已经优化到足以在 O(n) 中运行以进行简单的字符串匹配。
  • 这对我来说似乎是微优化 - 性能分析是否显示字符串替换是程序中最慢的部分?
  • 不,我没有对我的脚本进行性能分析,我只是确保我使用的是最快的可用函数。
  • 我做了一个 JSPerf 比较全局正则表达式和一个 for 循环:jsperf.com/javascript-replace-all。如果我正确地编写了测试,看起来答案是“视情况而定”。

标签: javascript string replace


【解决方案1】:

最简单的方法是使用带有g 标志的正则表达式来替换所有实例:

str.replace(/foo/g, "bar")

这会将字符串str 中所有出现的foo 替换为bar。如果您只有一个字符串,您可以将其转换为 RegExp 对象,如下所示:

var pattern = "foobar",
    re = new RegExp(pattern, "g");

【讨论】:

  • str.replace(/foo/g, "bar") 对我造成了错误。 str.replace(/foo/, "bar") 有效。
  • 警告:这不适用于包含换行符的字符串。 XRegExp 有一个替换方法可以解决问题。
  • 我内心的学究指出 OP 要求最快,而不是最简单
  • 我做了user.email.replace(/./g,','),整个电子邮件被替换为与电子邮件中字符相同数量的逗号。困惑...
  • @JaredTomaszewski,正则表达式中的句号(句点)字符代表“任何字符”。要表示实际的句号,您需要在它前面加上一个反斜杠,即 user.email.replace(/\./g,',')
【解决方案2】:

试试这个 replaceAll: http://dumpsite.com/forum/index.php?topic=4.msg8#msg8

String.prototype.replaceAll = function(str1, str2, ignore) 
{
    return this.replace(new RegExp(str1.replace(/([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g,"\\$&"),(ignore?"gi":"g")),(typeof(str2)=="string")?str2.replace(/\$/g,"$$$$"):str2);
} 

速度非常快,适用于所有这些条件 许多其他人都失败了:

"x".replaceAll("x", "xyz");
// xyz

"x".replaceAll("", "xyz");
// xyzxxyz

"aA".replaceAll("a", "b", true);
// bb

"Hello???".replaceAll("?", "!");
// Hello!!!

如果你能打破它,或者你有更好的东西,请告诉我,但要确保它可以通过这 4 个测试。

【讨论】:

  • 这对于替换未知内容的字符串来说是相当不错的,但是他的字符串是固定的,不需要转义正则表达式的复杂性。我提高了这个,因为我正在寻找一个 replaceAll 函数。
  • @jens 我所说的只是一个巨大的 CAVEAT EMPTOR。正则表达式非常复杂,一个随机的互联网答案应该可以一劳永逸地解决某人的问题,但很可能有一些潜伏的错误,这确实是一个糟糕的答案。我们不期望对明显正确的答案进行单元测试——当然不是。但我们确实希望,当没有经验的程序员可能被误导成错误的信心时,答案能让读者正确理解他们的风险。我的评论有助于警告那些初学者不要相信这个未经测试的疯狂代码。
  • 我已经开始在这个测试套件的底部为这个函数添加单元测试:github.com/agrothe/alphanumbers/blob/master/test/index.js
  • "x".replaceAll("", "xyz"); 的行为对我来说似乎是一个错误。如果我尝试替换空字符串,我真的会遇到错误。
  • 因为现在有一个标准的replaceAll 方法,这个答案现在覆盖它。请do monkey patching correctly首先检查属性的存在!
【解决方案3】:
var mystring = 'This is a string';
var newString = mystring.replace(/i/g, "a");

newString 现在是 'Thas as a strang'

【讨论】:

【解决方案4】:

你也可以试试:

string.split('foo').join('bar');

【讨论】:

  • 这个处理正则表达式吗?我猜。但是对于字符串到字符串的替换,这个是我最喜欢的 :) 使用 firefox 非常快
  • @yota 是的。您可以使用正则表达式。 "12px (2) bar-456-foo 44".split(/\d/).join("#")
  • 这对于最简单的情况来说是完美的。在一个不错的小功能中效果很好,例如:function replaceAll( s, f, r ){ return s.split( f ).join( r ); }。或者,如果您认为 RegEx 更快:function replaceAll( s, f, r ){ f = RegExp( f, 'gi' ); return s.replace( f, r ); }。然后就做foo = replaceAll( 'aaa', 'a', 'b' );
  • 最佳答案
  • 如果您的指针是包含一个或多个正则表达式保留字符的变量,那么其他答案的方法可以做意想不到的替换。这种方法的好处是它以相同的方式对待所有字符。
【解决方案5】:

您可以使用以下内容:

newStr = str.replace(/[^a-z0-9]/gi, '_');

newStr = str.replace(/[^a-zA-Z0-9]/g, '_');

这会将所有不是字母或数字的字符替换为('_')。只需更改下划线值即可替换您想要替换的任何内容。

【讨论】:

  • 应该是 .replace(/[a-zA-Z0-9]/g, '_') 没有 ^
【解决方案6】:

仅从速度问题考虑,我相信上面链接中提供的区分大小写的示例将是迄今为止最快的解决方案。

var token = "\r\n";
var newToken = " ";
var oldStr = "This is a test\r\nof the emergency broadcasting\r\nsystem.";
newStr = oldStr.split(token).join(newToken);

newStr 将是 “这是对紧急广播系统的考验。”

【讨论】:

    【解决方案7】:

    我认为真正的答案是它完全取决于您输入的内容。我创建了一个JsFiddle 来尝试其中的一堆以及我自己的几个来对抗各种输入。无论我如何看待结果,我都看不到明显的赢家。

    • RegExp 在所有测试用例中都不是最快的,但也不错。
    • Split/Join 方法似乎对于稀疏替换最快。
    • 我写的这个对于小输入和密集的来说似乎最快 替换:

      function replaceAllOneCharAtATime(inSource, inToReplace, inReplaceWith) {
          var output="";
          var firstReplaceCompareCharacter = inToReplace.charAt(0);
          var sourceLength = inSource.length;
          var replaceLengthMinusOne = inToReplace.length - 1;
          for(var i = 0; i < sourceLength; i++){
              var currentCharacter = inSource.charAt(i);
              var compareIndex = i;
              var replaceIndex = 0;
              var sourceCompareCharacter = currentCharacter;
              var replaceCompareCharacter = firstReplaceCompareCharacter;
              while(true){
                  if(sourceCompareCharacter != replaceCompareCharacter){
                  output += currentCharacter;
                  break;
              }
              if(replaceIndex >= replaceLengthMinusOne) {
                  i+=replaceLengthMinusOne;
                  output += inReplaceWith;
                  //was a match
                  break;
              }
              compareIndex++; replaceIndex++;
              if(i >= sourceLength){
                  // not a match
                  break;
              }
              sourceCompareCharacter = inSource.charAt(compareIndex)
                  replaceCompareCharacter = inToReplace.charAt(replaceIndex);
              }   
              replaceCompareCharacter += currentCharacter;
          }
          return output;
      }
      

    【讨论】:

    • 另一个需要考虑的因素是拆分/连接方法是最简单、最短、最中肯的方法,使其成为未来浏览器最有可能在内部对其进行优化的候选方法。在 JIST 编译期间,速度要快几倍(例如,它不会创建一个新数组和一个新字符串,而是只搜索并复制-n-粘贴它,与正则表达式线性相似)。
    【解决方案8】:

    像这样使用 Regex 对象

    var regex = new RegExp('"', 'g'); str = str.replace(regex, '\'');

    它将所有出现的" 替换为'

    【讨论】:

    • 在针是变量的情况下其他答案不起作用的地方,这个效果很好。
    【解决方案9】:

    什么是最快的我不知道,但我知道什么是最易读的——最短和最简单的。即使它比其他解决方案慢一点,也值得使用。

    所以使用:

     "string".replace("a", "b");
     "string".replace(/abc?/g, "def");
    

    享受好的代码而不是更快的代码(嗯...... 1/100000 秒没有区别)和丑陋的代码。 ;)

    【讨论】:

      【解决方案10】:

      我刚刚编写了一个基准代码并测试了前 3 个答案。 似乎对于短字符串( 投票第三多的答案比投票第二多的答案快。

      对于长字符串(在测试字符串中添加“.repeat(300)”),答案 1 后跟第二个和第三个更快。

      注意:

      上述情况适用于使用 v8 引擎(chrome/chromium 等)的浏览器。
      使用 firefox(SpiderMonkey 引擎)结果完全不同
      自己检查!! Firefox 的第三种解决方案似乎是
      第一个解决方案的速度比 Chrome 快 4.5 倍以上......疯了:D

      function log(data) {
        document.getElementById("log").textContent += data + "\n";
      }
      
      benchmark = (() => {
      
        time_function = function(ms, f, num) {
          var z;
          var t = new Date().getTime();
          for (z = 0;
            ((new Date().getTime() - t) < ms); z++) f(num);
          return (z / ms)
        } // returns how many times the function was run in "ms" milliseconds.
      
      
        function benchmark() {
          function compare(a, b) {
            if (a[1] > b[1]) {
              return -1;
            }
            if (a[1] < b[1]) {
              return 1;
            }
            return 0;
          }
      
          // functions
      
          function replace1(s) {
            s.replace(/foo/g, "bar")
          }
      
      String.prototype.replaceAll2 = function(_f, _r){ 
      
        var o = this.toString();
        var r = '';
        var s = o;
        var b = 0;
        var e = -1;
      //      if(_c){ _f = _f.toLowerCase(); s = o.toLowerCase(); }
      
        while((e=s.indexOf(_f)) > -1)
        {
          r += o.substring(b, b+e) + _r;
          s = s.substring(e+_f.length, s.length);
          b += e+_f.length;
        }
      
        // Add Leftover
        if(s.length>0){ r+=o.substring(o.length-s.length, o.length); }
      
        // Return New String
        return r;
      };
      
      String.prototype.replaceAll = function(str1, str2, ignore) {
            return this.replace(new RegExp(str1.replace(/([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g, "\\$&"), (ignore ? "gi" : "g")), (typeof(str2) == "string") ? str2.replace(/\$/g, "$$$$") : str2);
          }
      
          function replace2(s) {
            s.replaceAll("foo", "bar")
          }
      
          function replace3(s) {
            s.split('foo').join('bar');
          }
      
          function replace4(s) {
            s.replaceAll2("foo", "bar")
          }
      
      
          funcs = [
            [replace1, 0],
            [replace2, 0],
            [replace3, 0],
            [replace4, 0]
          ];
      
          funcs.forEach((ff) => {
            console.log("Benchmarking: " + ff[0].name);
            ff[1] = time_function(2500, ff[0], "foOfoobarBaR barbarfoobarf00".repeat(10));
            console.log("Score: " + ff[1]);
      
          })
          return funcs.sort(compare);
        }
      
        return benchmark;
      })()
      log("Starting benchmark...\n");
      res = benchmark();
      console.log("Winner: " + res[0][0].name + " !!!");
      count = 1;
      res.forEach((r) => {
        log((count++) + ". " + r[0].name + " score: " + Math.floor(10000 * r[1] / res[0][1]) / 100 + ((count == 2) ? "% *winner*" : "% speed of winner.") + " (" + Math.round(r[1] * 100) / 100 + ")");
      });
      log("\nWinner code:\n");
      log(res[0][0].toString());
      &lt;textarea rows="50" cols="80" style="font-size: 16; resize:none; border: none;" id="log"&gt;&lt;/textarea&gt;

      当您单击按钮时,测试将运行 10 秒(+2 秒)。

      我的结果(在同一台电脑上):

      Chrome/Linux Ubuntu 64:
      1. replace1 score: 100% *winner* (766.18)
      2. replace4 score: 99.07% speed of winner. (759.11)
      3. replace3 score: 68.36% speed of winner. (523.83)
      4. replace2 score: 59.35% speed of winner. (454.78)
      
      Firefox/Linux Ubuntu 64
      1. replace3 score: 100% *winner* (3480.1)
      2. replace1 score: 13.06% speed of winner. (454.83)
      3. replace4 score: 9.4% speed of winner. (327.42)
      4. replace2 score: 4.81% speed of winner. (167.46)
      

      好乱啊?

      冒昧添加更多测试结果

      Chrome/Windows 10
      1. replace1 score: 100% *winner* (742.49)
      2. replace4 score: 85.58% speed of winner. (635.44)
      3. replace2 score: 54.42% speed of winner. (404.08)
      4. replace3 score: 50.06% speed of winner. (371.73)
      
      Firefox/Windows 10
      1. replace3 score: 100% *winner* (2645.18)
      2. replace1 score: 30.77% speed of winner. (814.18)
      3. replace4 score: 22.3% speed of winner. (589.97)
      4. replace2 score: 12.51% speed of winner. (331.13)
      
      Edge/Windows 10
      1. replace1 score: 100% *winner* (1251.24)
      2. replace2 score: 46.63% speed of winner. (583.47)
      3. replace3 score: 44.42% speed of winner. (555.92)
      4. replace4 score: 20% speed of winner. (250.28)
      

      Galaxy Note 4 上的 Chrome

      1. replace4 score: 100% *winner* (99.82)
      2. replace1 score: 91.04% speed of winner. (90.88)
      3. replace3 score: 70.27% speed of winner. (70.15)
      4. replace2 score: 38.25% speed of winner. (38.18)
      

      【讨论】:

        【解决方案11】:

        在意识到我可能接近 10 年前编写的一个实现实际上并没有完全工作后,我尝试了其中的一些建议(在一个长期被遗忘的系统中的令人讨厌的生产错误,并非总是如此?!)...我注意到的是,我尝试过的那些(我没有全部尝试过)和我的有同样的问题,也就是说,它们不会取代每一次出现,只会取代第一次,至少对我来说通过将“..”替换为“.”来将“test..txt”变为“test.txt”的测试用例......也许我错过了正则表达式的情况?但我离题了...

        所以,我重写了我的实现如下。这非常简单,虽然我怀疑不是最快的,但我也不认为现代 JS 引擎的区别会很重要,除非你当然是在一个紧密的循环中这样做,但任何事情都是如此......

        function replaceSubstring(inSource, inToReplace, inReplaceWith) {
        
          var outString = inSource;
          while (true) {
            var idx = outString.indexOf(inToReplace);
            if (idx == -1) {
              break;
            }
            outString = outString.substring(0, idx) + inReplaceWith +
              outString.substring(idx + inToReplace.length);
          }
          return outString;
        
        }
        

        希望对某人有所帮助!

        【讨论】:

        • 如果 inToReplace 是 inReplaceWith 的子字符串,则不会工作。无限循环。
        【解决方案12】:
        // Find, Replace, Case
        // i.e "Test to see if this works? (Yes|No)".replaceAll('(Yes|No)', 'Yes!');
        // i.e.2 "Test to see if this works? (Yes|No)".replaceAll('(yes|no)', 'Yes!', true);
        String.prototype.replaceAll = function(_f, _r, _c){ 
        
          var o = this.toString();
          var r = '';
          var s = o;
          var b = 0;
          var e = -1;
          if(_c){ _f = _f.toLowerCase(); s = o.toLowerCase(); }
        
          while((e=s.indexOf(_f)) > -1)
          {
            r += o.substring(b, b+e) + _r;
            s = s.substring(e+_f.length, s.length);
            b += e+_f.length;
          }
        
          // Add Leftover
          if(s.length>0){ r+=o.substring(o.length-s.length, o.length); }
        
          // Return New String
          return r;
        };
        

        【讨论】:

        【解决方案13】:

        使用String 对象的replace() 方法。

        如所选答案中所述,应在正则表达式中使用 /g 标志,以替换字符串中子字符串的 所有 个实例。

        【讨论】:

        • 这只替换第一次出现的!
        • @Dr. Hfuhruhurr - 如果使用 /g 选项,它还可以替换所有匹配项,如 replace() 方法文档(例如 w3schools.com/jsref/jsref_obj_regexp.asp)所指定的。仅仅因为我没有明确提到 /g 选项并不会降低我的回答的有效性。
        【解决方案14】:

        @Gumbo 添加额外的答案 - user.email.replace(/foo/gi,"bar");

        /foo/g - Refers to the all string to replace matching the case sensitive
        
        /foo/gi - Refers to the without case sensitive and replace all For Eg: (Foo, foo, FoO, fOO)
        

        DEMO

        【讨论】:

          猜你喜欢
          • 2012-11-14
          • 1970-01-01
          • 1970-01-01
          • 2021-08-16
          • 2018-03-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多