Как заменить все вхождения строки в JavaScript

У меня есть эта строка:

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

дела

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

кажется, только удалить первое вхождение abc в строке выше. Как я могу заменить все вхождения этого?

84 ответа

Решение

Для полноты картины я задумался о том, какой метод я должен использовать для этого. Есть два основных способа сделать это, как предлагают другие ответы на этой странице.

Примечание. Как правило, расширение встроенных прототипов в 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 однако, я не уверен, насколько это повлияет на производительность (потенциально даже для строк, для которых экранирование не требуется, как для всех буквенно-цифровых строк).

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

В ответ на комментарий:

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

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

В ответ на комментарий Click Upvote вы можете упростить его еще больше:

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

Примечание. Регулярные выражения содержат специальные (мета) символы, и поэтому опасно слепо передавать аргумент в find Функция выше без предварительной обработки, чтобы избежать этих символов. Это описано в Руководстве JavaScript по регулярным выражениям в Mozilla Developer Network, где они представляют следующую служебную функцию:

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

Таким образом, чтобы сделать replaceAll() Функция выше безопаснее, ее можно изменить на следующую, если вы также включите escapeRegExp:

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

Примечание: не используйте это в реальном коде.

В качестве альтернативы регулярным выражениям для простой литеральной строки, вы можете использовать

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

Общая картина

str.split(search).join(replacement)

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

Используя регулярное выражение с g установленный флаг заменит все:

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

Смотрите здесь также

Вот функция-прототип строки, основанная на принятом ответе:

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/

Это самые распространенные и читаемые методы.

var str = "Test abc test test abc test test test abc test test abc"

Метод-01:

str = str.replace(/abc/g, "replaced text");

Метод-02:

str = str.split("abc").join("replaced text");

Метод-03:

str = str.replace(new RegExp("abc", "g"), "replaced text");

Метод-04:

while(str.includes("abc")){
    str = str.replace("abc", "replaced text");
}

Выход:

console.log(str);
// Test replaced text test test replaced text test test test replaced text test test replaced text

Обновить:

Уже несколько поздно для обновления, но так как я просто наткнулся на этот вопрос и заметил, что мой предыдущий ответ не тот, которым я доволен. Поскольку вопрос касался замены одного слова, невероятно, что никто не думал об использовании границ слов (\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 заменяет "" на ""

@ Майк, проверь ответ, который я там дал... Регулярное выражение - не единственный способ заменить несколько вхождений подстроки, это далеко не так. Думай гибко, думай о разделении!

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

Сопоставить с глобальным регулярным выражением:

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

Для замены однократного использования:

var res = str.replace('abc', "");

Для замены несколько раз используйте:

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

Или попробуйте функцию replaceAll отсюда:

Какие полезные методы JavaScript расширяют встроенные объекты?

str = str.replaceAll('abc', ''); OR

var search = 'abc';
str = str.replaceAll(search, '');

РЕДАКТИРОВАТЬ: разъяснения о заменеВсе наличие

Метод replaceAll добавлен в прототип String. Это означает, что он будет доступен для всех строковых объектов / литералов.

Например

var output = "test this".replaceAll('this', 'that');  //output is 'test that'.
output = output.replaceAll('that', 'this'); //output is 'test this'

С помощью RegExp в JavaScript может сделать работу за вас, просто сделайте что-то вроде ниже:

var str ="Test abc test test abc test test test abc test test abc";
str = str.replace(/abc/g, '');

Если вы думаете о повторном использовании, создайте функцию, которая сделает это за вас, но это не рекомендуется, так как это всего лишь одна строчная функция, но, опять же, если вы интенсивно ее используете, вы можете написать что-то вроде этого:

String.prototype.replaceAll = String.prototype.replaceAll || function(string, replaced) {
  return this.replace(new RegExp(string, 'g'), replaced);
};

и просто используйте его в своем коде снова и снова, как показано ниже:

var str ="Test abc test test abc test test test abc test test abc";
str = str.replaceAll('abc', '');

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

Скажем, вы хотите заменить все "abc" на "x":

let some_str = 'abc def def lom abc abc def'.split('abc').join('x')
console.log(some_str) //x def def lom x x def

Я пытался придумать что-то более простое, чем модификация прототипа строки.

Используйте регулярное выражение:

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

Производительность

Сегодня, 27.12.2019, провожу тесты на macOS v10.13.6 (High Sierra) для выбранных решений.

Выводы

  • В str.replace(/abc/g, '');(C) - хорошее кроссбраузерное быстрое решение для всех строк.
  • Решения на основе split-join(A,B) илиreplace(C,D) быстрые
  • Решения на основе while(E,F,G,H) медленные - обычно в ~4 раза медленнее для маленьких струн и примерно в ~3000 раз (!) Медленнее для длинных.
  • Решения повторения (RA,RB) медленные и не работают для длинных строк

Также создаю собственное решение. Похоже, что в настоящее время это самый короткий вариант, который выполняет задание:

str.split`abc`.join``

str = "Test abc test test abc test test test abc test test abc";
str = str.split`abc`.join``

console.log(str);

Детали

Тесты проводились в Chrome 79.0, Safari 13.0.4 и Firefox 71.0 (64 бит). ТестыRA а также RBиспользовать рекурсию. Полученные результаты

Короткая строка - 55 символов

Вы можете запустить тесты на своей машине ЗДЕСЬ. Результаты для Chrome:

Длинная строка: 275 000 символов

Рекурсивные решения RA и RB дают

RangeError: превышен максимальный размер стека вызовов

Для 1M символов они даже ломают Chrome

Я пытаюсь выполнить тесты для 1M символов для других решений, но E,F,G,H занимает так много времени, что браузер просит меня сломать скрипт, поэтому я сокращаю тестовую строку до 275K символов. Вы можете запустить тесты на своей машине ЗДЕСЬ. Результаты для Chrome

Код, используемый в тестах

var t="Test abc test test abc test test test abc test test abc"; // .repeat(5000)
var log = (version,result) => console.log(`${version}: ${result}`);


function A(str) {
  return str.split('abc').join('');
}

function B(str) {
  return str.split`abc`.join``; // my proposition
}


function C(str) {
  return str.replace(/abc/g, '');
}

function D(str) {
  return str.replace(new RegExp("abc", "g"), '');
}

function E(str) {
  while (str.indexOf('abc') !== -1) { str = str.replace('abc', ''); }
  return str;
}

function F(str) {
  while (str.indexOf('abc') !== -1) { str = str.replace(/abc/, ''); }
  return str;
}

function G(str) {
  while(str.includes("abc")) { str = str.replace('abc', ''); }
  return str;
}

// src: https://stackru.com/a/56989553/860099
function H(str)
{
    let i = -1
    let find = 'abc';
    let newToken = '';

    if (!str)
    {
        if ((str == null) && (find == null)) return newToken;
        return str;
    }

    while ((
        i = str.indexOf(
            find, i >= 0 ? i + newToken.length : 0
        )) !== -1
    )
    {
        str = str.substring(0, i) +
            newToken +
            str.substring(i + find.length);
    }
    return str;
}

// src: https://stackru.com/a/22870785/860099
function RA(string, prevstring) {
  var omit = 'abc';
  var place = '';
  if (prevstring && string === prevstring)
    return string;
  prevstring = string.replace(omit, place);
  return RA(prevstring, string)
}

// src: https://stackru.com/a/26107132/860099
function RB(str) {
  var find = 'abc';
  var replace = '';
  var i = str.indexOf(find);
  if (i > -1){
    str = str.replace(find, replace);
    i = i + replace.length;
    var st2 = str.substring(i);
    if(st2.indexOf(find) > -1){
      str = str.substring(0,i) + RB(st2, find, replace);
    }
  }
  return str;
}




log('A ', A(t));
log('B ', B(t));
log('C ', C(t));
log('D ', D(t));
log('E ', E(t));
log('F ', F(t));
log('G ', G(t));
log('H ', H(t));
log('RA', RA(t)); // use reccurence
log('RB', RB(t)); // use reccurence
<p style="color:red">This snippet only presents codes used in tests. It not perform test itself!<p>

Замена одинарных кавычек:

function JavaScriptEncode(text){
    text = text.replace(/'/g,'&apos;')
    // More encode here if required

    return text;
}
str = str.replace(new RegExp("abc", 'g'), "");

работал лучше для меня, чем приведенные выше ответы. так new RegExp("abc", 'g') создает RegExp, который соответствует всему вхождению ('g' флаг) текста ("abc"). Вторая часть - это то, что заменяется, в вашем случае пустой строкой ("").str это строка, и мы должны переопределить ее, как replace(...) просто возвращает результат, но не переопределяет. В некоторых случаях вы можете использовать это.

// зациклить, пока число вхождений не достигнет 0. ИЛИ просто скопировать / вставить

    function replaceAll(find, replace, str) 
    {
      while( str.indexOf(find) > -1)
      {
        str = str.replace(find, replace);
      }
      return str;
    }

Это самая быстрая версия, которая не использует регулярные выражения.

Пересмотренный jsperf

replaceAll = function(string, omit, place, prevstring) {
  if (prevstring && string === prevstring)
    return string;
  prevstring = string.replace(omit, place);
  return replaceAll(prevstring, omit, place, string)
}

Это почти в два раза быстрее, чем метод split и join.

Как указано в комментарии здесь, это не будет работать, если ваш omit переменная содержит place, как в: replaceAll("string", "s", "ss"), потому что он всегда сможет заменить другое вхождение слова.

На моей рекурсивной замене есть еще один jsperf с вариантами, которые идут еще быстрее ( http://jsperf.com/replace-all-vs-split-join/12)!

  • Обновление от 27 июля 2017 года. Похоже, что RegExp теперь обладает самой высокой производительностью в недавно выпущенном Chrome 59.

Если то, что вы хотите найти, уже находится в строке, и у вас нет под рукой оператора regex escaper, вы можете использовать join/split:

    function replaceMulti(haystack, needle, replacement)
    {
        return haystack.split(needle).join(replacement);
    }

    someString = 'the cat looks like a cat';
    console.log(replaceMulti(someString, 'cat', 'dog'));

String.prototype.replaceAll- ECMAScript 2021

Новый String.prototype.replaceAll()Метод возвращает новую строку, в которой все совпадения с шаблоном заменены на замену. Шаблон может быть строкой или регулярным выражением, а замена может быть строкой или функцией, вызываемой для каждого совпадения.

      const message = 'bo dady bo';
const messageWithSpace = message.replaceAll('bo', 'go')

console.log(message);
function replaceAll(str, find, replace) {
  var i = str.indexOf(find);
  if (i > -1){
    str = str.replace(find, replace); 
    i = i + replace.length;
    var st2 = str.substring(i);
    if(st2.indexOf(find) > -1){
      str = str.substring(0,i) + replaceAll(st2, find, replace);
    }       
  }
  return str;
}

Конечно, в 2021 году правильный ответ:

Строка.прототип.replaceAll()

Поддержка браузера:

IE 9+ (проверено на IE11)
Все остальные браузеры (после 2012 г.)

Результат такой же, как и у родной replaceAll в случае ввода первого аргумента:
null, undefined, Object, Function, Date, ... , RegExp, Number, String, ...

Ссылка: https://tc39.es/ecma262/#sec-string.prototype.replaceall+ синтаксис RegExp

Важное примечание: как отмечают некоторые профессионалы, многие рекурсивные функции, предложенные в ответах, вернут неверный результат. (Попробуйте их со случаем Challenged приведенного выше фрагмента)
Возможно, некоторые хитрые методы, такие как .split('searchValue').join('replaceValue')или Некоторые хорошо управляемые функции дают тот же результат, но определенно гораздо более низкую производительность, чем native replaceAll()/ polyfill replaceAll()/ replace() + RegExp


Другие методы назначения полифилла

Наивно, но поддержите даже пожилых (лучше избегать)

Например, мы можем поддерживать и IE7+, не используя Object.defineProperty() и используя мой старый наивный метод присваивания:

      if (!String.prototype.replaceAll) {
    String.prototype.replaceAll = function(search, replace) { // <-- Naive method for assignment
        // ... (Polyfill code Here)
    }
}

И он должен хорошо работать для базового использования в IE7+.
Но , как здесь объяснил @sebastian-simon, это может создать второстепенные проблемы в случае более продвинутого использования. Например:

      for (var k in 'hi') console.log(k);
// 0
// 1
// replaceAll  <-- ?
Полностью надежный, но тяжелый

На самом деле предложенный мной вариант несколько оптимистичен. Как мы доверили, среда (браузер/узел) определенно предназначена примерно для 2012-2021 годов, а также является стандартной/известной, поэтому не требует особого рассмотрения.
Но могут быть и более старые браузеры ИЛИ какие-то неожиданные проблемы, а полифилы по-прежнему могут поддерживать и решать больше возможных проблем среды. Поэтому, если нам нужна максимально возможная поддержка, мы можем использовать polyfill librariesкак:

https://polyfill.io/

Специально для replaceAll:

      <script src="https://polyfill.io/v3/polyfill.min.js?features=String.prototype.replaceAll"></script>

Мне нравится этот метод (выглядит немного чище):

text = text.replace(new RegExp("cat","g"), "dog"); 

Простейший способ сделать это без использования регулярных выражений - это разделение и соединение, как здесь:

var str="Test abc test test abc test test test abc test test abc";
str.split('abc').join('')
var str = "ff ff f f a de def";
str = str.replace(/f/g,'');
alert(str);

http://jsfiddle.net/ANHR9/

while (str.indexOf('abc') !== -1)
{
    str = str.replace('abc', '');
}

Если строка содержит аналогичный шаблон, как abccc, вы можете использовать это:

str.replace(/abc(\s|$)/g, "")

После нескольких испытаний и множества неудач я обнаружил, что приведенная ниже функция кажется лучшей универсальной, когда дело доходит до сопоставимости браузеров и простоты использования. Это единственное рабочее решение для старых браузеров, которое я нашел. (Да, хотя старый браузер не рекомендуется и устарел, некоторые устаревшие приложения по-прежнему активно используют браузеры OLE (например, старые приложения VB6 или макросы Excel xlsm с формами)

В любом случае, вот простая функция.

      function replaceAll(str, match, replacement){
   return str.split(match).join(replacement);
}

По состоянию на август 2020 года в ECMAScript есть предложение Stage 4, которое добавляет replaceAll способ String.

Теперь он поддерживается в Chrome 85+, Edge 85+, Firefox 77+, Safari 13.1+.

Использование такое же, как и replace метод:

String.prototype.replaceAll(searchValue, replaceValue)

Вот пример использования:

'Test abc test test abc test.'.replaceAll('abc', 'foo'); // -> 'Test foo test test foo test.'

Он поддерживается в большинстве современных браузеров, но существуют полифилы:

Он поддерживается двигателем V8 за экспериментальным флагом.--harmony-string-replaceall. Подробнее читайте на сайте V8.

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

str.replace(/your_regex_pattern/g, replacement_string);

Пример:

var str = "Test abc test test abc test test test abc test test abc";

var res = str.replace(/[abc]+/g, "");

console.log(res);

Другие вопросы по тегам