Как заменить символ по определенному индексу в JavaScript?


544

Допустим, у меня есть строка, Hello worldи мне нужно заменить символ в индексе 3. Как я могу заменить символ, указав индекс?

var str = "hello world";

Мне нужно что-то вроде

str.replaceAt(0,"h");

131
Что странно, это то, что str[0] = 'x', похоже, не выдает никаких ошибок, но не дает желаемого эффекта!
Майкл

6
@ Майкл, с помощью которого вы получите индекс в 0, установите его в 'x', этот оператор сам по себе вернет новое значение; 'Икс'. но все это не меняет оригинал, так что это совершенно верно, просто не то, что вы ожидали. это не ссылка
Пол Scheltema

8
@ Майкл это делает, если "use strict"активирован: Uncaught TypeError: Cannot assign to read only property '0' of string 'hello world'(по крайней мере, в браузерах webkit)
Jan Turo

1
Строки Javascript являются неизменными, их нельзя изменить «на месте», поэтому вы не можете изменить один символ. Фактически, каждое вхождение одной и той же строки является ОДНЫМ объектом.
Мартин Шеффер

хорошо, смотрите ответ: D
Мартин Шеффер

Ответы:


611

В JavaScript строки являются неизменяемыми , что означает, что лучшее, что вы можете сделать, - это создать новую строку с измененным содержимым и назначить переменную, указывающую на нее.

Вам нужно определить replaceAt()функцию самостоятельно:

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

И используйте это так:

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

101
Обратите внимание, что обычно не стоит расширять базовые классы JavaScript. Вместо этого используйте простую служебную функцию.
Атес Горал

86
Я должен спросить почему? Для этого есть поддержка прототипов.
Джем Калионку

30
@CemKalyoncu: он может заставить код играть плохо с другими библиотеками. Но я никогда не был укушен этим сам.
Алекс

6
@alex: вы правы в этом, вам действительно нужно проверить, существует ли эта функция, прежде чем делать это. Но все же, даже если это так, он должен делать точно такую ​​же операцию.
Джем Калионку

80
Я не согласен, даже если вы обнаружите, существует ли эта функция, более поздняя библиотека все равно может использовать / создать аналогичную функцию - 2 плохие идеи создают худшую. В общем, просто никогда не играйте с кодом других людей (включая основную систему). Создать новую функцию.
мученик

93

В replaceAtJavaScript нет функции. Вы можете использовать следующий код для замены любого символа в любой строке в указанной позиции:

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.substr(0,index) + chr + str.substr(index+1);
}
<button onclick="rep();">click</button>



Я предпочитаю это решение, потому что вы можете заменить один символ с ним. Решение с наибольшим количеством голосов не работает, если вы хотите заменить только один символ. Работает только при замене двух символов! Кроме того, substr следует заменить на подстроку, как упоминалось во многих других комментариях.
gignu

74

Ты не можешь Возьмите символы до и после позиции и добавьте новую строку:

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

На самом деле, да, вы можете, но это было бы излишним. Вы можете настроить регулярное выражение, чтобы пропустить первый индекс - 1 символ и соответствовать символу в индексе. Вы можете использовать String.replace с этим регулярным выражением для прямой замены на месте. Но это излишество. Таким образом, на практике вы не можете. Но в теории вы можете.
Атес Горал

12
@Ates: функция замены не выполняет замену на месте, она создает новую строку и возвращает ее.
Гуффа

2
МДН предлагает использовать String.prototype.substringсверх String.prototype.substr.
Mikemike

33

Здесь много ответов, и все они основаны на двух методах:

  • МЕТОД 1: разбить строку, используя две подстроки и вставить символ между ними
  • МЕТОД 2: преобразовать строку в массив символов, заменить один элемент массива и присоединиться к нему

Лично я бы использовал эти два метода в разных случаях. Позволь мне объяснить.

@FabioPhms: твой метод был тем, который я использовал изначально, и я боялся, что он плох для строки с большим количеством символов. Тем не менее, вопрос в том, что много персонажей? Я проверил это на 10 параграфах "lorem ipsum", и это заняло несколько миллисекунд. Затем я проверил это на 10-кратной струне - не было большой разницы. Гектометр

@vsync, @Cory Mawhorter: Ваши комментарии однозначны; однако, опять же, что такое большая строка? Я согласен, что для 32 ... 100 КБ производительность должна быть лучше, и для этой операции замены символов следует использовать substring-вариант.

Но что произойдет, если мне придется сделать немало замен?

Мне нужно было провести свои собственные тесты, чтобы доказать, что быстрее в этом случае. Допустим, у нас есть алгоритм, который будет манипулировать относительно короткой строкой, состоящей из 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*
}

Я создал для этого скрипку, и она здесь . Есть два теста, TEST1 (подстрока) и TEST2 (преобразование массива).

Результаты:

  • ТЕСТ 1: 195мс
  • ТЕСТ 2: 6 мс

Кажется, преобразование массива превосходит подстроку на 2 порядка! Итак - что, черт возьми, здесь произошло ???

На самом деле происходит то, что все операции в TEST2 выполняются над самим массивом с использованием выражения присваивания, например strarr2[p] = n. Назначение действительно быстрое по сравнению с подстрокой на большой строке, и ясно, что оно победит.

Итак, все дело в выборе правильного инструмента для работы. Снова.


7
Ваш тест перенесен в jsperf, и результаты оказались совершенно другими: jsperf.com/string-replace-character . Это все о выборе правильного инструмента для работы;)
Коллин

1
@colllin: я абсолютно согласен. Так что я почти буквально клонировал тесты от jsfiddle до jsperf. Однако, кроме использования правильного инструмента, вы должны сделать это правильным образом. Обратите внимание на проблему, мы говорим о том, что произойдет, если нам придется сделать немало замен в алгоритме. Конкретный пример включает в себя 10000 замен. Это означает, что один тест должен включать 10000 замен. Таким образом, используя jsperf, я получил довольно последовательные результаты, см .: jsperf.com/… .
OzrenTkalcecKrznaric

1
Вот почему я не люблю JavaScript. такие частые, интуитивные, очевидные варианты использования не предусмотрены. Вы можете подумать, что создатели javascript могут подумать, что люди могут захотеть заменить символы в строках и что они решат использовать удобные методы в языке.
Анбизкад

1
@colllin сделал что-то отличное от OzrenTkalcecKrznaric. OzrenTkalcecKrznaric сделал предположение, что строка является статической, и разделение может быть оптимизировано перед запуском. Что не обязательно верно. если вам нужно разбивать при каждом запуске, то подход с подстрокой будет примерно в два-четыре раза быстрее для маленьких строк, и только быстрее, чем больше будет строка. По моему мнению, это делает подстроку лучшей реализацией для большинства случаев использования, за исключением большого количества изменений в большой статической строке.
WiR3D

3
Проблема в том, что на test2 split и join находятся вне цикла for, где вы сравниваете замену одного символа с множественным (несправедливо), первый метод быстрее для одиночных замен, а второй лучше для цепных замен один за другим.
Θεόφιλος Μουρατίδης

32

Работа с векторами обычно наиболее эффективна для связи со String.

Я предлагаю следующую функцию:

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);


9
это плохо для больших строк, не нужно создавать массив с сумасшедшим количеством ячеек, если вы можете просто использовать substr для его обрезки ...
vsync

1
Полезно, если вам нужно изменить много символов, и это просто.
Sheepy

6
Я создал тест на jsperf: jsperf.com/replace-character-by-index - Substr намного быстрее и согласован с 32 байтами до 100 КБ. Я проголосовал за это, и, как бы мне ни было больно это говорить, хотя это и звучит лучше, substr - лучший выбор для строк любого размера.
Кори Моухортер

Это совершенно неоптимально.
Радько Динев

Это хорошее быстрое решение, если вы знаете, что собираетесь использовать небольшие строки. Если у вас все в порядке с расширением прототипов, то вы можете добиться аналогичного поведения в 2 строки:Array.prototype.replace = function(index, val) { this[index] = val; return this; }; 'word'.split('').replace(1, '0').join(''); // returns 'w0rd'
Майкл Плаутц

29

В Javascript строки неизменны, поэтому вы должны сделать что-то вроде

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

Заменить символ в x в i с 'h'


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

4
Ясно и просто, но в настоящее время не будет работать с эмодзи (например, 😀) при использовании splitиjoin
Green

1
С es6 я думаю, что Array.from (str) может быть лучшим вариантом сейчас, но я пришел сюда с этим знанием и узнал кое-что столь же сжатое от вас, так что спасибо!
Дэвид Камер

7

Однострочное использование 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
})

6

Этот метод хорош для небольших строк, но может быть медленным для большого текста.

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

/* 
  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. 
*/

3

@CemKalyoncu: Спасибо за отличный ответ!

Я также немного адаптировал его, чтобы сделать его более похожим на метод Array.splice (и учел примечание @Ates):

spliceString=function(string, index, numToDelete, char) {
      return string.substr(0, index) + char + string.substr(index+numToDelete);
   }

var myString="hello world!";
spliceString(myString,myString.lastIndexOf('l'),2,'mhole'); // "hello wormhole!"

Не уверен, почему это было отклонено. Это законный ответ. Это работает, как указано. И это согласуется с Array.spliceфункциональностью типа.
Скримоти

3

Это работает аналогично Array.splice:

String.prototype.splice = function (i, j, str) {
    return this.substr(0, i) + str + this.substr(j, this.length);
};

3

Вы могли бы попробовать

var strArr = str.split("");

strArr[0] = 'h';

str = strArr.join("");

2

Если вы хотите заменить символы в строке, вы должны создать изменяемые строки. По сути, это массивы символов. Вы можете создать фабрику:

  function MutableString(str) {
    var result = str.split("");
    result.toString = function() {
      return this.join("");
    }
    return result;
  }

Затем вы можете получить доступ к символам, и весь массив преобразуется в строку при использовании в качестве строки:

  var x = MutableString("Hello");
  x[0] = "B"; // yes, we can alter the character
  x.push("!"); // good performance: no new string is created
  var y = "Hi, "+x; // converted to string: "Hi, Bello!"

2

Обобщая ответ Афанасия Куракина, мы имеем:

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заявление, чтобы определить, собираемся ли мы вернуть origCharили newChar.


2

Вы можете расширить тип строки, включив метод inset:

String.prototype.append = function (index,value) {
  return this.slice(0,index) + value + this.slice(index);
};

var s = "New string";
alert(s.append(4,"complete "));

Затем вы можете вызвать функцию:


1

Я сделал функцию, которая делает что-то похожее на то, что вы спрашиваете, он проверяет, находится ли символ в строке в массиве недопустимых символов, если он заменяет его на ''

    var validate = function(value){
        var notAllowed = [";","_",">","<","'","%","$","&","/","|",":","=","*"];
        for(var i=0; i<value.length; i++){
            if(notAllowed.indexOf(value.charAt(i)) > -1){
               value = value.replace(value.charAt(i), "");
               value = validate(value);
            }
       }
      return value;
   }

1

это легко достижимо с RegExp!

const str = 'Hello RegEx!';
const index = 11;
const replaceWith = 'p';

//'Hello RegEx!'.replace(/^(.{11})(.)/, `$1p`);
str.replace(new RegExp(`^(.{${ index }})(.)`), `$1${ replaceWith }`);

//< "Hello RegExp"

1

Вот версия, которую я придумал, если вы хотите стилизовать слова или отдельные символы в их индексе в реагировать / javascript.

replaceAt( yourArrayOfIndexes, yourString/orArrayOfStrings ) 

Рабочий пример: https://codesandbox.io/s/ov7zxp9mjq

function replaceAt(indexArray, [...string]) {
    const replaceValue = i => string[i] = <b>{string[i]}</b>;
    indexArray.forEach(replaceValue);
    return string;
}

А вот еще один альтернативный метод

function replaceAt(indexArray, [...string]) {
    const startTag = '<b>';
    const endTag = '</b>';
    const tagLetter = i => string.splice(i, 1, startTag + string[i] + endTag);
    indexArray.forEach(tagLetter);
    return string.join('');
}

И другой...

function replaceAt(indexArray, [...string]) {
    for (let i = 0; i < indexArray.length; i++) {
        string = Object.assign(string, {
          [indexArray[i]]: <b>{string[indexArray[i]]}</b>
        });
    }
    return string;
}

0

Я знаю, что это старо, но решение не работает для отрицательного индекса, поэтому я добавляю к нему патч. надеюсь, это поможет кому-то

String.prototype.replaceAt=function(index, character) {
    if(index>-1) return this.substr(0, index) + character + this.substr(index+character.length);
    else return this.substr(0, this.length+index) + character + this.substr(index+character.length);

}

0

Допустим, вы хотите заменить Kthиндекс (индекс на основе 0) на 'Z'. Вы можете использовать Regexдля этого.

var re = var re = new RegExp("((.){" + K + "})((.){1})")
str.replace(re, "$1A$`");

Любая идея, почему это помечено не полезно?
KSP

Я не проверял, работает ли это регулярное выражение, но, возможно, его опровергают, потому что, вероятно, намного быстрее создать новую строку, чем компилировать конечный автомат (регулярное выражение), что очень дорого.
Фело Вильчес

0

Вы можете использовать следующую функцию для замены Characterили Stringв определенной позиции строки. Для замены всех следующих совпадений используйте String.prototype.replaceAllMatches()функцию.

String.prototype.replaceMatch = function(matchkey, replaceStr, matchIndex) {
    var retStr = this, repeatedIndex = 0;
    for (var x = 0; (matchkey != null) && (retStr.indexOf(matchkey) > -1); x++) {
        if (repeatedIndex == 0 && x == 0) {
            repeatedIndex = retStr.indexOf(matchkey);
        } else { // matchIndex > 0
            repeatedIndex = retStr.indexOf(matchkey, repeatedIndex + 1);
        }
        if (x == matchIndex) {
            retStr = retStr.substring(0, repeatedIndex) + replaceStr + retStr.substring(repeatedIndex + (matchkey.length));
            matchkey = null; // To break the loop.
        }
    }
    return retStr;
};

Тестовое задание:

var str = "yash yas $dfdas.**";

console.log('Index Matched replace : ', str.replaceMatch('as', '*', 2) );
console.log('Index Matched replace : ', str.replaceMatch('y', '~', 1) );

Вывод:

Index Matched replace :  yash yas $dfd*.**
Index Matched replace :  yash ~as $dfdas.**

-1

Вот мое решение с использованием троичного оператора и оператора карты. Более читаемый, понятный конец, легче понять, если вы спросите меня.

Это больше в es6 и лучшие практики.

function replaceAt() {
  const replaceAt = document.getElementById('replaceAt').value;

  const str = 'ThisIsATestStringToReplaceCharAtSomePosition';
  const newStr = Array.from(str).map((character, charIndex) => charIndex === (replaceAt - 1) ? '' : character).join('');

  console.log(`New string: ${newStr}`);
}
<input type="number" id="replaceAt" min="1" max="44" oninput="replaceAt()"/>


-3

Методы здесь сложны. Я бы сделал это так:

var myString = "this is my string";
myString = myString.replace(myString.charAt(number goes here), "insert replacement here");

Это так просто, как только может.


6
Не совсем. Фактически это заменит первое появление персонажа. Все еще не так.
Томаш Зато - Восстановить Монику
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.