如何在JavaScript中替换特定索引处的字符?

763

我有一个字符串,比如说 Hello world,我需要替换索引为3的字符。我该怎么通过指定索引来替换一个字符?

var str = "hello world";

我需要类似于:

str.replaceAt(0,"h");

253
有趣的是,str[0] = 'x' 看起来没有抛出任何错误,但却没有产生预期的效果! - Michael
9
如果你使用@Michael并得到索引为0的值,并将其设置为'x',那么该语句本身将返回新值'x'。但所有这些操作都不会改变原来的值,因此它仍然是有效的,只是不符合你的期望。这不是一个引用。 - Paul Scheltema
17
如果启用了"use strict",则会出现以下错误:在字符串'hello world'的只读属性'0'上不能进行赋值操作(至少在Webkit浏览器中)。 - Jan Turoň
9
JavaScript字符串是不可变的,它们无法“原地”修改,因此您不能修改单个字符。事实上,相同字符串的每个出现都是一个对象。 - Martijn Scheffer
30个回答

825

在JavaScript中,字符串是不可变的,这意味着你所能做的最好的办法就是创建一个新的带有更改内容的字符串,并将变量指向它。

你需要自己定义replaceAt()函数:

String.prototype.replaceAt = function(index, replacement) {
    return this.substring(0, index) + replacement + this.substring(index + replacement.length);
}

然后像这样使用它:

var hello = "Hello World";
alert(hello.replaceAt(2, "!!")); // He!!o World

177
请注意,通常不建议扩展基本的JavaScript类。相反,请使用普通的实用函数。 - Ates Goral
99
为什么?原型支持就是为了这个目的。 - Cem Kalyoncu
41
它有可能使代码与其他库产生问题。但我自己从未遇到过这种情况。 - alex
8
你说得对,确实需要在执行操作前先检查该函数是否存在。但即使函数存在,它也应该执行完全相同的操作。 - Cem Kalyoncu
109
我不同意,即使你检测到函数是否存在,后续库仍然可能使用/创建类似的函数——两个错误想法会让事情变得更糟。总的来说,永远不要随意改动别人的代码(包括核心系统),而是应该创建一个新的函数。 - martyman
显示剩余18条评论

160

JavaScript 中没有 replaceAt 函数。您可以使用以下代码在指定位置替换任何字符串中的任何字符:

function rep() {
    var str = 'Hello World';
    str = setCharAt(str,4,'a');
    alert(str);
}

function setCharAt(str,index,chr) {
    if(index > str.length-1) return str;
    return str.substring(0,index) + chr + str.substring(index+1);
}
<button onclick="rep();">click</button>


我更喜欢这个解决方案,因为你可以用它替换单个字符。得票最多的解决方案如果你只想替换一个字符是行不通的。它只适用于替换两个字符!此外,如许多其他评论中所提到的,substr 应该被替换为 substring。 - gignu
1
在我的测试中,这种方法是迄今为止最快的...而且如果您不需要验证[index]是否大于字符串长度,则速度会更快。修改后:function setCharAt(str,idx,newChr){ return str.substring(0,idx)+newChr+str.substring(idx+1);} - ashleedawg
1
@ScottTesler - 这个答案使用的是 substring,而不是 substr... 你提供的链接中哪里说这两个函数是“遗留”或应该被避免使用的?substringsubstr 有着不同的用途 - ashleedawg
@ashleedawg 看起来它不再被弃用了。 - Scott Tesler

120

你无法直接删除一个字符串中的单个字符。可以获取该位置前后的字符并将它们连接成一个新字符串:

var s = "Hello world";
var index = 3;
s = s.substring(0, index) + 'x' + s.substring(index + 1);

实际上,你可以这样做,但是这太过浪费。你可以设置一个正则表达式来跳过前面索引(index)-1 个字符并匹配索引处的字符。你可以使用该正则表达式的 String.replace 函数进行直接替换。但这太浪费了。因此,在实践中你不能这样做。但从理论上讲,你可以这样做。 - Ates Goral
15
replace函数不会进行原地替换,它会创建一个新的字符串并返回它。 - Guffa
4
MDN建议使用String.prototype.substring替代String.prototype.substr。 - Michał Terczyński

78
str = str.split('');
str[3] = 'h';
str = str.join('');

9
简明扼要,但目前在使用“split”和“join”时无法处理表情符号(例如)的问题。 - Green
4
我认为使用ES6的Array.from(str)现在可能是更好的选择,但我带着这个知识来到这里,从你那里学到了同样简洁的内容,非常感谢你! - David Kamer
1
@DavidKamer 我来晚了,但是似乎Array.fromstr.split慢得多。https://blog.shovonhasan.com/never-use-array-from-to-convert-strings-to-arrays/ - Charles
@Green 如果您的字符串包含表情符号,那么它很可能是用户输入,因此我认为没有明显的理由使用这种方法更改字符串中的字符。如果您想要在输入转换场景中更改其中一部分,应该使用带有正则表达式的replace方法。 - vdegenne
1
@KadenSkinner 这是一个数组,所以你可以使用 str.splice(3, h.length, ...h.split('')) 或者 str.splice.apply(str, [3, h.length].concat(h.split(''))), 但与 str.substring(0,3) + h + str.substring(5) 相比,这似乎有些疯狂。 - colllin
显示剩余3条评论

36

这里有很多答案,所有答案都基于两种方法:

  • 方法1:使用两个子字符串拆分字符串并在它们之间插入字符
  • 方法2:将字符串转换为字符数组,替换一个数组成员并连接起来

就我个人而言,我会根据不同情况使用这两种方法。让我来解释一下。

@FabioPhms:你的方法是我最初使用的方法,我担心它对于具有大量字符的字符串而言效果不佳。但问题是什么是大量字符?我在10个“lorem ipsum”段落上进行了测试,只花了几毫秒。然后我在10倍大的字符串上进行了测试——确实没有太大区别。唔。

@vsync,@Cory Mawhorter:您的评论很明确;但是,再次提问,什么是大字符串?我同意,在32...100kb的情况下,性能应该更好,并且应该使用substring-variant来进行此操作的字符替换。

但是如果我必须进行相当多的替换会发生什么呢?

我需要进行自己的测试以证明在这种情况下哪个更快。假设我们有一个算法,它将操纵由1000个字符组成的相对较短的字符串。我们期望在该字符串中的每个字符平均被替换约100次。因此,测试这样的代码:

var str = "... {A LARGE STRING HERE} ...";

for(var i=0; i<100000; i++)
{
  var n = '' + Math.floor(Math.random() * 10);
  var p = Math.floor(Math.random() * 1000);
  // replace character *n* on position *p*
}

我创建了一个JSFiddle作为演示,并且它在这里。 有两个测试,TEST1(子字符串)和TEST2(数组转换)。

结果:

  • TEST1:195ms
  • TEST2:6ms

看起来数组转换比子字符串快了两个数量级!那么 - 到底发生了什么?

实际上,TEST2中的所有操作都是在数组本身上完成的,使用类似strarr2[p] = n的赋值表达式。相对于大型字符串的子字符串,赋值是非常快的,显然会胜出。

所以,关键是选择适合当前任务的工具。再次强调。


7
将你的测试移到了jsperf,结果有了很大的不同:http://jsperf.com/string-replace-character。关键在于选择正确的工具来完成工作 ;) - colllin
1
@colllin:我完全同意。所以我几乎是从jsfiddle克隆了测试到jsperf。然而,除了使用正确的工具外,您还必须以正确的方式进行操作。注意问题,我们正在讨论如果在算法中需要进行相当多的替换会发生什么。具体示例包括10000个替换。这意味着一个测试应该包括10000个替换。因此,使用jsperf,我得到了相当一致的结果,请参见:http://jsperf.com/replace-a-character-at-a-particular-index-in-javascript。 - OzrenTkalcecKrznaric
1
@colllin 对 OzrenTkalcecKrznaric 做了一些不同的事情。OzrenTkalcecKrznaric 假设字符串是静态的,并且在运行之前可以优化拆分。这并不一定正确。如果您必须在每次运行时拆分,则子字符串方法在小字符串上大约快两倍到四倍,并且随着字符串变得越来越大,它只会更快。在我看来,这使得子字符串成为大多数用例的更好实现,除非在大型静态字符串上有大量更改。 - WiR3D
4
问题在于,在test2中,split和join在for循环之外。在这里,您将单个字符替换与多个字符替换进行比较(不公平)。第一种方法适用于单个替换,而第二种方法适用于连续的替换。 - Theofilos Mouratidis
1
这是旧的,但应该是... 我看到了和@ΘεόφιλοςΜουρατίδης一样的东西,但我自己检查了一下(为每个字符串制作了一个转换为数组的版本...仍然Test#2更好(虽然不是很多)(请参见http://jsfiddle.net/TomerW/rsjop8yw/21/) - Tomer W
显示剩余5条评论

35

处理向量通常最有效的方法是使用字符串。

我建议使用以下函数:

String.prototype.replaceAt=function(index, char) {
    var a = this.split("");
    a[index] = char;
    return a.join("");
}

运行此代码片段:

String.prototype.replaceAt=function(index, char) {
    var a = this.split("");
    a[index] = char;
    return a.join("");
}

var str = "hello world";
str = str.replaceAt(3, "#");

document.write(str);


11
这对于较大的字符串是不利的,如果可以使用substr进行裁剪,就无需创建一个具有大量单元格的数组... - vsync
1
如果你需要更改很多字符,这个工具非常有用且简单。 - Sheepy
7
我在jsperf上创建了一个测试:http://jsperf.com/replace-character-by-index -- Substr比较快,并且从32个字节一直到100kb都很稳定。我赞同这个结论,尽管我不太情愿承认,即使感觉上使用其他方法更好,但substr仍然是任何大小的字符串的更好选择。 - Cory Mawhorter
这相当不优化。 - Radko Dinev
这是一个很好的快速解决方案,如果你知道你将使用小字符串。如果你可以扩展原型,那么你可以用两行代码实现类似的行为:Array.prototype.replace = function(index, val) { this[index] = val; return this; }; 'word'.split('').replace(1, '0').join(''); // 返回 'w0rd' - Michael Plautz

34

在JavaScript中,字符串是不可变的,因此你必须像这样做:

var x = "Hello world"
x = x.substring(0, i) + 'h' + x.substring(i+1);

将x中第i个字符替换为'h'


20

function dothis() {
  var x = document.getElementById("x").value;
  var index = document.getElementById("index").value;
  var text = document.getElementById("text").value;
  var length = document.getElementById("length").value;
  var arr = x.split("");
  arr.splice(index, length, text);
  var result = arr.join("");
  document.getElementById('output').innerHTML = result;
  console.log(result);
}
dothis();
<input id="x" type="text" value="White Dog" placeholder="Enter Text" />
<input id="index" type="number" min="0"value="6" style="width:50px" placeholder="index" />
<input id="length" type="number" min="0"value="1" style="width:50px" placeholder="length" />
<input id="text" type="text" value="F" placeholder="New character" />
<br>
<button id="submit" onclick="dothis()">Run</button>
<p id="output"></p>

这种方法适用于小长度的字符串,但对于较大的文本可能会变慢。

var x = "White Dog";
var arr = x.split(""); // ["W", "h", "i", "t", "e", " ", "D", "o", "g"]
arr.splice(6, 1, 'F');

/* 
  Here 6 is starting index and 1 is no. of array elements to remove and 
  final argument 'F' is the new character to be inserted. 
*/
var result = arr.join(""); // "White Fog"

1
因为我发现额外的描述很有用,实现简单而强大,所以我点了赞。谢谢!:-D - Seth Eden
1
Array.from 对于字符串转换为数组也是一个不错的选择。 - Valen
是的,Array.from 在这里也可以使用。但 Array.from 是在 ES2015 中添加的,在旧版浏览器中没有支持。此外,我个人更喜欢使用 split 函数,因为它可以更自由地操作字符串模式。 - Vikramaditya
1
如果你只想替换一个字符,可以使用arr[6]='F';而不是.splice() - Jay Dadhania
你所建议的已经被@fabio-phms在上面的回答中提出。我只是从另一个角度来看,我们有灵活性替换一个或多个字符。 - Vikramaditya

18

使用String.replace和回调函数的一行代码(不支持表情符号):

// 0 - index to replace, 'f' - replacement string
'dog'.replace(/./g, (c, i) => i == 0? 'f': c)
// "fog"

解释:

//String.replace will call the callback on each pattern match
//in this case - each character
'dog'.replace(/./g, function (character, index) {
   if (index == 0) //we want to replace the first character
     return 'f'
   return character //leaving other characters the same
})

1
简洁美观 - 一个不强制扩展原型或创建新函数的绝佳选择。谢谢! - Brian Powell

11

综合Afanasii Kurakin的回答,我们有:

function replaceAt(str, index, ch) {
    return str.replace(/./g, (c, i) => i == index ? ch : c);
}

let str = 'Hello World';
str = replaceAt(str, 1, 'u');
console.log(str); // Hullo World

让我们扩展并解释一下正则表达式和替换函数:

function replaceAt(str, index, newChar) {
    function replacer(origChar, strIndex) {
        if (strIndex === index)
            return newChar;
        else
            return origChar;
    }
    return str.replace(/./g, replacer);
}

let str = 'Hello World';
str = replaceAt(str, 1, 'u');
console.log(str); // Hullo World

正则表达式 . 匹配一个字符。使用 g 会使其在循环中匹配每个字符。replacer 函数被调用时同时给出原始字符和该字符在字符串中的索引。我们进行简单的 if 语句来确定是否返回 origCharnewChar


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接