题 如何在JavaScript中替换所有出现的字符串?


我有这个字符串:

"Test abc test test abc test test test abc test test abc"

str = str.replace('abc', '');

似乎只删除了第一次出现的 abc 在上面的字符串中。我怎样才能更换 所有 发生了吗?


3173
2017-07-17 17:53


起源


stackoverflow.com/questions/1137436/... - Canavar
简单应用: var str = 'abcabcbabc'; str.replace(/a/g, 'x'); --> 'xbcxbcxbc - FedericoCapaldo
请注意,只要您要替换的字符串不包含特殊的正则表达式字符,Federico的选项就很棒。 - Andrew
@Andrew特殊正则表达式字符可以转义为普通字符。 - modiX
对于任何想知道的人,你可以通过反斜杠来逃避正则表达式。例如,str.replace(/ \ _ / g)应该可以工作,但不能使用str.replace(/ \ t / g),因为']'是一个特殊的正则表达式字符。 - ashish-goel


答案:


为了完整起见,我开始考虑使用哪种方法来完成这项工作。根据本页其他答案的建议,基本上有两种方法可以做到这一点。

注意: 通常,通常不建议在JavaScript中扩展内置原型。我提供String原型的扩展仅仅是为了说明的目的,显示了假设标准方法的不同实现 String 内置原型。


基于正则表达式的实现

String.prototype.replaceAll = function(search, replacement) {
    var target = this;
    return target.replace(new RegExp(search, 'g'), replacement);
};

拆分和加入(功能)实现

String.prototype.replaceAll = function(search, replacement) {
    var target = this;
    return target.split(search).join(replacement);
};

在效率方面我不太了解正则表达式如何在幕后工作,我倾向于倾向于分裂并在过去加入实现而不考虑性能。当我确实想知道哪个更有效率,以及在什么边缘时,我用它作为借口来找出答案。

在我的Chrome Windows 8计算机上, 基于正则表达式的实现是最快的随着 拆分和连接实现速度慢了53%。这意味着正则表达式的速度是我使用的lorem ipsum输入的两倍。

看看这个 基准 相互运行这两个实现。


正如@ThomasLeduc和其他人在下面的评论中指出的那样,基于正则表达式的实现可能存在问题,如果 search 包含保留为的某些字符 正则表达式中的特殊字符。该实现假定调用者将事先转义字符串,或者只传递没有表中字符的字符串 常用表达 (MDN)。

MDN还提供了一种逃避字符串的实现。如果这也被标准化,那将是很好的 RegExp.escape(str),但唉,它不存在:

function escapeRegExp(str) {
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
}

我们可以打电话 escapeRegExp 在我们的 String.prototype.replaceAll 但是,我不确定这会对性能产生多大影响(甚至可能对于不需要转义的字符串,例如所有字母数字字符串)。


1635
2017-07-12 01:46



我可以问一下,当替换所有字符串时,g字符是什么意思? - jonney
在Android 4.1上,regexp方法的速度提高了15%,但是你没有逃避要搜索的表达式,因此这个基准测试是不完整的。 - andreszs
此解决方案存在问题,如果您的搜索字符串似乎包含正则表达式表达式的特殊字符,则会对其进行解释。我推荐@Sandy Unitedwolf回答。 - Thomas Leduc
第一个会做到这一点:'bla.bla'.replaceAll('。','_'); “_______”。第二个将做'bla_bla',这更通常是你想要做的。 - RobKohr
光泽的答案。以5倍多的票数转到下一个“不正确”标记的答案。那就是你想要的。 - HoldOffHunger


str = str.replace(/abc/g, '');

回应评论:

var find = 'abc';
var re = new RegExp(find, 'g');

str = str.replace(re, '');

回应 单击“Upvote”的评论,你可以更简化它:

function replaceAll(str, find, replace) {
    return str.replace(new RegExp(find, 'g'), replace);
}

注意: 正则表达式包含特殊(元)字符,因此盲目地传递参数是危险的 find 上面的函数没有预处理它来逃避这些字符。这包含在 Mozilla开发者网络关于正则表达式的JavaScript指南,它们提供以下实用功能:

function escapeRegExp(str) {
    return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}

所以为了做到 replaceAll() 如果您还包括,则可以将其修改为以下功能 escapeRegExp

function replaceAll(str, find, replace) {
    return str.replace(new RegExp(escapeRegExp(find), 'g'), replace);
}

3597
2017-07-17 17:54



正则表达式是实现“开箱即用”的唯一方法,而无需实现自己的'replaceAll'方法。我不是仅仅为了使用它们而建议使用它们。 - Sean Bright
这个评论是我自己的功能: function replaceAll(find, replace,str) { var re = new RegExp(find, 'g'); str = str.replace(re, replace); return str; } - Click Upvote
重要的警告 replaceAll 实现是,如果,它将无法工作 find 包含元字符。 - Martin Ender
@SeanBright你是对的。我在php代码中使用了你的javascript,所以我不得不添加一个额外的反斜杠来逃避。在小提琴上你的代码是完美的。我应该检查一下。道歉 jsfiddle.net/7y19xyq8 - Onimusha
@Bennett是的 扩展JavaScript本机类型原型的不良做法。 - Emile Bergeron


注意:不要在实际代码中使用它。

作为简单文字字符串的正则表达式的替代,您可以使用

str = "Test abc test test abc test...".split("abc").join("");

一般模式是

str.split(search).join(replacement)

在某些情况下,这比使用更快 replaceAll 和正则表达式,但在现代浏览器中似乎不再是这种情况。因此,这应该只是用作快速入侵,以避免需要转义正则表达式,而不是真正的代码。


1193
2017-07-17 20:29



它让我感到惊讶,因为我希望这种方法能够分配更多的对象,产生更多的垃圾,因此需要更长的时间,但是当我在浏览器中进行实际测试时,这种方法始终比接受的响应快20%。当然,结果可能会有所不同。 - MgSam
我自己很好奇,并设置了这个: jsperf.com/replace-all-vs-split-join 。与其他javascript引擎相比,似乎v8在分裂/加入数组时速度很快。 - fabi
非常好 - 在传递特殊字符时也可以避免出现错误的RegExps。我在对象属性中添加“查找并替换它”的通用标志,但是如果我需要替换“。”则担心。或“}”并忘了我3个月后使用RegEx! - tobriand
在我的ipad2 / safari上,分割/连接比替换慢65%,因此您的结果可能会有所不同。 - mrk
并作为String.prototype: String.prototype.replaceAll = function(f,r){return this.split(f).join(r);}。用法: "My string".replaceAll('st', ''); 制作“我的戒指” - MacroMan


使用正则表达式 g 标志集将替换所有:

someString = 'the cat looks like a cat';
anotherString = someString.replace(/cat/g, 'dog');
// anotherString now contains "the dog looks like a dog"

另见


507
2018-05-06 23:18



有点傻我认为,但JS全局正则表达式是进行多次替换的唯一方法。 - Mike
技术上你可以循环 var sourceText 计算实例数(numOfInstances使用 substring 或分割和计算的长度(以及其他策略) thatWhichYouWantToReplace 然后做 for (var i = 0; i < numOfInstances; i++){ sourceText = sourceText.replace('thatWhichYouWantToReplace', ''); 或者甚至更简单地使用while循环(while sourceText.indexOf(thatWhichYouWantToReplace > -1){ sourceText = sourceText.replace(...))但我不明白为什么你在使用时会想那样做 /g是如此简单,可能更高效。 - Zargold


这是一个基于接受的答案的字符串原型函数:

String.prototype.replaceAll = function (find, replace) {
    var str = this;
    return str.replace(new RegExp(find, 'g'), replace);
};

编辑 

如果你的 find 将包含特殊字符,然后您需要转义它们:

String.prototype.replaceAll = function (find, replace) {
    var str = this;
    return str.replace(new RegExp(find.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'), 'g'), replace);
};

小提琴: http://jsfiddle.net/cdbzL/


90
2018-02-11 23:03



但是如果 find 包含像 . 要么 $ 在正则表达式中有特殊含义? - callum
@callum在这种情况下,您必须转义find变量(请参阅上面的编辑)。 - jesal
不要修改您不拥有的对象 - Aamir Afridi
jesal,这是我遇到的字符串替换问题的一个很好的解决方案,它显然克服了JavaScript中字符串的“不变性”。您或其他人可以解释一下这个原型函数如何覆盖字符串的不变性吗?这个 不 工作;我只想了解它所带来的这个辅助问题。 - Tom
@Tom:它 不 克服不变性: var str = this 创建对不可变字符串的第二个引用 replace 应用方法,然后返回一个 新的不可变字符串。这些原型函数返回一个新的不可变字符串,如果没有,你就可以编写 someStrVar.replaceAll('o', '0'); 和 someStrVar 会改变的。相反,你必须写 someStrVar = someStrVar.replaceAll('o', '0'); < - 重新分配以设置var以保存 新的不可变字符串。没有办法解决这个问题。试试控制台: x = 'foobar'; x.replaceAll('o', '0'); x; - Elias Van Ootegem


更新:

这是一个更新有点迟,但因为我偶然发现了这个问题,并注意到我以前的答案不是我很满意的答案。由于问题涉及替换单个单词,所以没有人想到使用单词边界(\b

'a cat is not a caterpillar'.replace(/\bcat\b/gi,'dog');
//"a dog is not a caterpillar"

这是一个简单的正则表达式,可以避免在大多数情况下替换部分单词。但是,一个破折号 - 仍然被认为是一个词边界。因此在这种情况下可以使用条件来避免替换字符串 cool-cat

'a cat is not a cool-cat'.replace(/\bcat\b/gi,'dog');//wrong
//"a dog is not a cool-dog" -- nips
'a cat is not a cool-cat'.replace(/(?:\b([^-]))cat(?:\b([^-]))/gi,'$1dog$2');
//"a dog is not a cool-cat"

基本上,这个问题和这里的问题是一样的: Javascript将“'”替换为“''”

@Mike,检查我给出的答案... regexp不是替换多次出现的替换的唯一方法,远非它。思考灵活,思考分裂!

var newText = "the cat looks like a cat".split('cat').join('dog');

或者,为了防止替换单词部分 - 批准的答案也会这样做!你可以使用正则表达式来解决这个问题,我承认,这些正则表达式稍微复杂一些,并且作为结果,也有点慢:

var regText = "the cat looks like a cat".replace(/(?:(^|[^a-z]))(([^a-z]*)(?=cat)cat)(?![a-z])/gi,"$1dog");

输出与接受的答案相同,但是,在此字符串上使用/ cat / g表达式:

var oops = 'the cat looks like a cat, not a caterpillar or coolcat'.replace(/cat/g,'dog');
//returns "the dog looks like a dog, not a dogerpillar or cooldog" ?? 

哎呀,这可能不是你想要的。那是什么?恕我直言,一个只有条件地取代'猫'的正则表达式。 (即不是单词的一部分),如下:

var caterpillar = 'the cat looks like a cat, not a caterpillar or coolcat'.replace(/(?:(^|[^a-z]))(([^a-z]*)(?=cat)cat)(?![a-z])/gi,"$1dog");
//return "the dog looks like a dog, not a caterpillar or coolcat"

我的猜测是,这符合您的需求。当然,这不是全面的,但它应该足以让你开始。我建议在这些页面上阅读更多内容。这对于完善此表达式以满足您的特定需求非常有用。

http://www.javascriptkit.com/jsref/regexp.shtml

http://www.regular-expressions.info


最后补充:

鉴于这个问题仍然有很多观点,我想我可能会添加一个例子 .replace 与回调函数一起使用。在这种情况下,它大大简化了表达式  提供更大的灵活性,例如替换正确的大写或替换两者 cat 和 cats 一气呵成:

'Two cats are not 1 Cat! They\'re just cool-cats, you caterpillar'
   .replace(/(^|.\b)(cat)(s?\b.|$)/gi,function(all,char1,cat,char2)
    {
       //check 1st, capitalize if required
       var replacement = (cat.charAt(0) === 'C' ? 'D' : 'd') + 'og';
       if (char1 === ' ' && char2 === 's')
       {//replace plurals, too
           cat = replacement + 's';
       }
       else
       {//do not replace if dashes are matched
           cat = char1 === '-' || char2 === '-' ? cat : replacement;
       }
       return char1 + cat + char2;//return replacement string
    });
//returns:
//Two dogs are not 1 Dog! They're just cool-cats, you caterpillar

75
2018-03-01 10:02



我认为另外有趣的部分应放在底部。 ps:我刚才注意到第一行的一半不在该区域,让我允许修复它!


匹配全局正则表达式:

anotherString = someString.replace(/cat/g, 'dog');

45
2018-05-06 23:23