ChatGPT解决这个技术问题 Extra ChatGPT

替换字符串中字符的所有实例的最快方法[重复]

这个问题在这里已经有了答案:如何替换 JavaScript 中所有出现的字符串(79 个答案)2 年前关闭。社区在 11 个月前审查了是否重新打开此问题并将其关闭:需要详细信息或澄清添加详细信息并通过编辑此帖子来澄清问题。

在 JavaScript 中替换字符串中字符串/字符的所有实例的最快方法是什么? whilefor 循环、正则表达式?

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

M
Martin Tournoij

最简单的方法是使用带有 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,',')
C
Colorfully Monochrome

试试这个 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首先检查属性的存在!
S
Sani Singh Huttunen
var mystring = 'This is a string';
var newString = mystring.replace(/i/g, "a");

newString 现在是 'Thas as a strang'


此例程在 Firefox 上是最快的,但在 chrome 上非常慢:检查我的答案:stackoverflow.com/a/57697050/236062
V
Vlada

您也可以尝试:

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' );
最佳答案
如果您的针是包含一个或多个正则表达式保留字符的变量,那么其他答案的方法可以做意想不到的替换。这种方法的好处是它以相同的方式对待所有字符。
s
ssamuel68

您可以使用以下内容:

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

或者

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

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


应该是 .replace(/[a-zA-Z0-9]/g, '_') 没有 ^
D
DamienS

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

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 将是“这是对紧急广播系统的测试”。


R
Rick Velde

我认为真正的答案是它完全取决于您输入的内容。我创建了一个 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 替换索引 = 0; var sourceCompareCharacter = currentCharacter; var replaceCompareCharacter = firstReplaceCompareCharacter; while(true){ if(sourceCompareCharacter != replaceCompareCharacter){ output += currentCharacter;休息; } if(replaceIndex >= replaceLengthMinusOne) { i+=replaceLengthMinusOne;输出 += inReplaceWith; //是一个比赛中断; } 比较索引++;替换索引++; if(i >= sourceLength){ // 不是匹配中断; } sourceCompareCharacter = inSource.charAt(compareIndex) replaceCompareCharacter = inToReplace.charAt(replaceIndex); } 替换比较字符 += 当前字符; } 返回输出; }


另一个需要考虑的因素是拆分/连接方法是最简单、最短、最直接的方法,这使其成为未来浏览器最有可能在内部将其优化为快几倍的候选方法(例如,而不是创建一个新数组和一个新字符串,而是在 JIST 编译期间搜索并复制-n-粘贴它线性地类似于正则表达式)。
N
Neel Kamal

像这样使用正则表达式对象

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

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


在针是变量的情况下,其他答案不起作用的地方,这个效果很好。
C
Crozin

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

所以使用:

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

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


Z
Zibri

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

对于长字符串(将“.repeat(300)”添加到测试字符串)更快的是答案 1,然后是第二个和第三个。

笔记:

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

函数日志(数据){ 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) } // 返回函数在“ms”毫秒内运行了多少次 function benchmark() { function compare(a, b) { if (a[1] > b[1]) { return -1; } if (a[1] < b[1]) { return 1; } return 0; } // 函数 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; } // 添加剩余部分 if(s.length >0){ r+=o.substring(o.length-s.length, o.length); } // 返回新字符串 return r; }; String.prototype.replaceAll = function(str1, str2, ignore) { return this.replace(new RegExp(str1.replace(/([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\< \>\-\&])/g, "\\$&"), (忽略? "gi" : "g")), (typeof(str2) == "string") ? str2.replace(/\$/g, "$$$$") : str2); } 函数replace2(s) { s.replaceAll("foo", "bar") } 函数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("基准测试:" + ff[0].name); ff[1] = time_function(2500, ff[0], "foOfoobarBaR barbarfoobarf00".repeat(10) ); console.log("分数:" + ff[1]); }) return funcs.sort(compare); } 返回基准; })() log("开始基准测试...\n");水库=基准(); console.log("获胜者:" + res[0][0].name + " !!!");计数 = 1; res.forEach((r) => { log((count++) + "." + r[0].name +" score:" + Math.floor(10000 * r[1] / res[0][1] ) / 100 + ((count == 2) ? "% *winner*" : "% 获胜者速度。") + " (" + Math.round(r[1] * 100) / 100 + ")") ; }); log("\n中奖代码:\n");日志(res[0][0].toString());

当您单击按钮时,测试将运行 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)

F
Frank W. Zammetti

在意识到我可能接近 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 的子字符串,则不会工作。无限循环。
M
MadHatter
// 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;
};

此例程在 Chrome 上是第二快的,但它的速度是 Firefox 上最快的 4.5 倍,请查看我的答案:stackoverflow.com/a/57697050/236062
j
jmealy

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

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


这只取代了第一次出现!
@博士。 Hfuhruhurr - 如果使用 /g 选项,它也可以替换所有匹配项,如 replace() 方法文档(例如 w3schools.com/jsref/jsref_obj_regexp.asp)所指定。仅仅因为我没有明确提到 /g 选项并不会降低我的答案的有效性。
S
Surya R Praveen

@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