Pytanie Jak zamienić wszystkie wystąpienia ciągu znaków w JavaScript?


Mam ten ciąg:

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

Robić

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

zdaje się usuwać tylko pierwsze wystąpienie abc w powyższym łańcuchu. Jak mogę wymienić wszystko wystąpienia tego?


3173
2017-07-17 17:53


pochodzenie


stackoverflow.com/questions/1137436/... - Canavar
SIMPLEST: var str = 'abcabcbabc'; str.replace(/a/g, 'x'); --> 'xbcxbcxbc - FedericoCapaldo
Uważaj, że opcja Federico jest świetna, o ile twój ciąg do zamiany nie zawiera specjalnych znaków regularnych. - Andrew
@Andrew Specjalne znaki wyrażeń regularnych mogą być znakami normalnymi. - modiX
Dla każdego, kto się zastanawia, możesz uciec przed wyrażeń regularnych przez ukośnik odwrotny. Na przykład powinien działać str.replace (/ \] / g), ale nie str.replace (/) / g), ponieważ ']' jest specjalnym znakiem regularnym. - ashish-goel


Odpowiedzi:


Ze względu na kompletność, zastanawiałem się, którą metodę zastosować w tym celu. Zasadniczo można to zrobić na dwa sposoby, jak sugerują inne odpowiedzi na tej stronie.

Uwaga: Generalnie nie zaleca się rozszerzania wbudowanych prototypów w JavaScript. Dostarczam jako rozszerzenia na prototypie String po prostu dla celów ilustracyjnych, pokazując różne implementacje hipotetycznej standardowej metody na String wbudowany prototyp.


Wdrażanie oparte na regularnym wyrażaniu

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

Podział i przyłączenie (funkcjonalne) Wdrożenie

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

Nie wiedząc zbyt wiele o tym, jak wyrażenia regularne działają za kulisami pod względem wydajności, zwykle skłaniałem się do dzielenia i dołączania do implementacji w przeszłości bez myślenia o wydajności. Kiedy zastanawiałem się, która z nich jest bardziej skuteczna i z jakiego marginesu, użyłem jej jako wymówki, aby się dowiedzieć.

Na moim komputerze z systemem Windows 8, implementacja oparta na wyrażeniach regularnych jest najszybsza, z Implementacja podziału i dołączenia jest o 53% wolniejsza. Oznacza to, że wyrażenia regularne są dwa razy szybsze dla wejścia Lorem ipsum, którego użyłem.

Sprawdź to reper uruchamianie tych dwóch implementacji względem siebie.


Jak zauważono w komentarzu poniżej @ThomasLeduc i innych, może pojawić się problem z implementacją opartą na wyrażeniach regularnych, jeśli search zawiera pewne znaki, które są zarezerwowane jako znaki specjalne w wyrażeniach regularnych. Implementacja zakłada, że ​​osoba wywołująca zejdzie wcześniej z ciągu znaków lub przekaże tylko ciągi bez znaków w tabeli Wyrażenia regularne (MDN).

MDN zapewnia również implementację, która ucieka z naszych łańcuchów. Byłoby miło, gdyby to było również znormalizowane jako RegExp.escape(str), ale niestety, nie istnieje:

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

Moglibyśmy zadzwonić escapeRegExp w naszym String.prototype.replaceAll Wdrożenie, jednak nie jestem pewien, jak bardzo wpłynie to na wydajność (potencjalnie nawet dla ciągów, dla których ucieczka nie jest potrzebna, podobnie jak wszystkie ciągi alfanumeryczne).


1635
2017-07-12 01:46



Czy mogę zapytać, czym jest g-znak, gdy zastępuję wszystkie wystąpienia ciągu? - jonney
W systemie Android 4.1 metoda regexp jest o 15% szybsza, ale nie wymyka się wyrażeniu do wyszukania, więc ten test porównawczy jest niekompletny. - andreszs
Występuje problem z tym rozwiązaniem, jeśli szukany ciąg znaków zawiera znaki specjalne wyrażenia regexp, zostaną one zinterpretowane. Polecam odpowiedź @Sandy Unitedwolf. - Thomas Leduc
Pierwszy zrobi to: 'bla.bla'.replaceAll ('. ',' _ '); "_______". Drugi zrobi "bla_bla", co jest bardziej ogólne, co chcesz zrobić. - RobKohr
Połysk odpowiedzi. Przejdź do następnej "niepoprawnej" odpowiedzi z pięcioma kolejnymi głosami. To jest to czego chcesz. - HoldOffHunger


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

W odpowiedzi na komentarz:

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

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

W odpowiedzi na Kliknij Upvotekomentarz, możesz uprościć go jeszcze bardziej:

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

Uwaga: Wyrażenia regularne zawierają specjalne znaki (meta) i jako takie niebezpiecznie jest ślepo przekazywać argumenty w find funkcja powyżej bez wstępnego przetworzenia, aby uniknąć tych znaków. Obejmuje to Mozilla Developer Network„s Przewodnik po języku JavaScript w wyrażeniach regularnych, gdzie przedstawiają następującą funkcję użyteczności:

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

Tak więc, aby zrobić replaceAll() funkcja powyżej bezpieczniej, może być modyfikowana do następujących, jeśli również uwzględnisz escapeRegExp:

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

3597
2017-07-17 17:54



Wyrażenia regularne to jedyny sposób na osiągnięcie tego "po wyjęciu z pudełka" bez wdrożenia własnej metody "replaceAll". Nie sugeruję ich użycia tylko ze względu na ich użycie. - Sean Bright
To jest moja własna funkcja z tego komentarza: function replaceAll(find, replace,str) { var re = new RegExp(find, 'g'); str = str.replace(re, replace); return str; } - Click Upvote
Główne zastrzeżenie replaceAll Wdrożenie polega na tym, że nie zadziała, jeśli find zawiera metaznaki. - Martin Ender
@SeanBright masz rację. Użyłem twojego javascript w kodzie php, więc musiałem dodać dodatkowy ukośnik odwrotny aby uciec. Na skrzypcach twój kod jest doskonały. Powinienem był sprawdzić. Przeprosiny jsfiddle.net/7y19xyq8 - Onimusha
@Bennett to jest Zła praktyka polegająca na rozszerzeniu prototypu rodzimych typów JavaScript. - Emile Bergeron


Uwaga: nie używaj tego w prawdziwym kodzie.

Jako alternatywę dla wyrażeń regularnych dla prostego ciągu literałowego możesz użyć

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

Ogólny wzór jest

str.split(search).join(replacement)

W niektórych przypadkach było to szybsze niż używanie replaceAll i wyrażenie regularne, ale w nowoczesnych przeglądarkach to już nie wygląda. Tak więc powinno to być używane tylko jako szybki hack, aby uniknąć ucieczki od wyrażenia regularnego, a nie w prawdziwym kodzie.


1193
2017-07-17 20:29



Zaskoczyło mnie to, ponieważ spodziewałem się, że ta metoda będzie przydzielała więcej obiektów, tworząc więcej śmieci, a więc trwa dłużej, ale kiedy faktycznie testowałem w przeglądarce, ta metoda była zawsze o około 20% szybsza niż przyjęta odpowiedź. Oczywiście wyniki mogą się różnić. - MgSam
Byłem ciekaw siebie i skonfigurowałem to: jsperf.com/replace-all-vs-split-join . Wygląda na to, że v8 jest po prostu szalony szybko przy dzieleniu / łączeniu tablic w porównaniu do innych silników javascript. - fabi
Bardzo ładne - również oszczędza ci możliwość złych RegExps podczas przekazywania znaków specjalnych. Dodaję ogólną flagę "znajdź i zamień ją" we właściwości obiektu, ale obawiałem się, że muszę zastąpić "." lub "}" i zapomniałem, że używałem RegEx 3 miesiące później! - tobriand
Na moim ipad2 / safari split / join jest o 65% wolniejszy od zamiany, więc twoje wyniki mogą się różnić. - mrk
I jako String.prototype: String.prototype.replaceAll = function(f,r){return this.split(f).join(r);}. Stosowanie: "My string".replaceAll('st', ''); produkuje "Mój pierścień" - MacroMan


Używanie wyrażenia regularnego z g Zestaw flag zastąpi wszystkie:

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

Zobacz także tutaj


507
2018-05-06 23:18



Głupie, myślę, ale globalne wyliczenie JS to jedyny sposób na wielokrotne zastępowanie. - Mike
dobrze technicznie można przechodzić var sourceText policz liczbę instancji (numOfInstances) za pomocą substring lub podzielić i policzyć długość (spośród innych strategii) thatWhichYouWantToReplace następnie zrób for (var i = 0; i < numOfInstances; i++){ sourceText = sourceText.replace('thatWhichYouWantToReplace', ''); } lub jeszcze łatwiej po prostu użyj pętli while (while sourceText.indexOf(thatWhichYouWantToReplace > -1){ sourceText = sourceText.replace(...)) ale nie rozumiem, dlaczego chciałbyś to zrobić w ten sposób podczas używania /g jest tak łatwy i prawdopodobnie bardziej wydajny. - Zargold


Oto prototypowa funkcja łańcuchowa oparta na zaakceptowanej odpowiedzi:

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

EDYTOWAĆ 

Jeżeli twój find będzie zawierać znaki specjalne, a następnie musisz im uciec:

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

Skrzypce: http://jsfiddle.net/cdbzL/


90
2018-02-11 23:03



Ale co gdyby find zawiera znaki takie jak . lub $ które mają specjalne znaczenie w regex? - callum
@callum W takim przypadku musisz uciec z szukanej zmiennej (patrz edycja powyżej). - jesal
Nie modyfikuj obiektów, które nie są Twoją własnością - Aamir Afridi
jesal, jest to wspaniałe rozwiązanie napotkanego problemu z zamianą ciągów i najwyraźniej pokonuje "niezmienność" napisów w JavaScript. Czy ty lub ktoś inny mógłbyś wyjaśnić, w jaki sposób ta prototypowa funkcja unieważnia niezmienność łańcuchów? To robi praca; Chcę tylko zrozumieć to dodatkowe pytanie, jakie ono stwarza. - Tom
@ Tom: To nie robi przezwyciężyć niezmienność w ogóle: var str = this tworzy drugie odniesienie do niezmiennego łańcucha, do którego replace jest zastosowana metoda, która z kolei zwraca wartość a nowy niezmienny ciąg. te prototypowe funkcje zwracają nowy niezmienny ciąg znaków, jeśli nie, będziesz mógł pisać someStrVar.replaceAll('o', '0'); i someStrVar zostanie zmieniony. Zamiast tego musisz napisać someStrVar = someStrVar.replaceAll('o', '0'); <- ponowne przypisanie, aby ustawić zmiennik do przechowywania nowy niezmienny ciąg. nie da się tego obejść. Spróbuj w konsoli: x = 'foobar'; x.replaceAll('o', '0'); x; - Elias Van Ootegem


Aktualizacja:

Trochę za późno na aktualizację, ale ponieważ natknąłem się na to pytanie i zauważyłem, że moja poprzednia odpowiedź nie jest tym, z czego jestem zadowolony. Ponieważ pytanie dotyczyło zastąpienia jednego słowa, to nikt nie pomyślał o używaniu granic słów (\b)

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

Jest to proste wyrażenie regularne, które w większości przypadków zapobiega zastępowaniu części słów. Jednak myślnik - wciąż jest uważana za granicę słowa. W tym przypadku można użyć warunkowych, aby uniknąć zamiany ciągów takich jak 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"

w zasadzie to pytanie jest takie samo jak pytanie tutaj: JavaScript zastępuje "" "przez" ""

@Mike, sprawdź odpowiedź, którą podałem tam ... regexp nie jest jedynym sposobem na zastąpienie wielu wystąpień subskrypcji, z dala od niego. Myśl elastycznie, myśl podziel!

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

Ewentualnie, aby zapobiec zastąpieniu części wyrazów, które zrobi zatwierdzona odpowiedź! Możesz obejść ten problem, używając wyrażeń regularnych, które, jak przyznaję, są nieco bardziej złożone i jako wynik tego, odrobinę wolniejsze:

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

Wynik jest taki sam jak przyjęta odpowiedź, jednak za pomocą wyrażenia / cat / g w tym ciągu:

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" ?? 

W rzeczy samej, oczywiście, to nie jest to, czego potrzebujesz. Co to jest? IMHO, wyrażenie, które warunkowo zastępuje "kot". (tzn. nie jest częścią słowa), tak jak poniżej:

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"

Zgaduję, że to spełnia twoje potrzeby. Oczywiście nie jest to całkowicie odporne, ale powinno wystarczyć. Polecam lekturę nieco więcej na tych stronach. Będzie to przydatne w doskonaleniu tego wyrażenia, aby spełnić Twoje szczególne potrzeby.

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

http://www.regular-expressions.info


Ostateczny dodatek:

Biorąc pod uwagę, że to pytanie wciąż ma wiele poglądów, pomyślałem, że mogę dodać przykład .replace używany z funkcją zwrotną. W tym przypadku radykalnie upraszcza to wyrażenie i zapewnia jeszcze większą elastyczność, na przykład zastąpienie poprawnymi wielkimi literami lub zastąpienie obu cat i cats za jednym razem:

'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



Myślę, że dodatkowa interesująca część powinna być umieszczona na dole. ps .: Zauważyłem, że połowa pierwszej linii jest już poza obszarem, pozwól mi to naprawić!


Dopasuj do globalnego wyrażenia regularnego:

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

45
2018-05-06 23:23





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

Lub wypróbuj tutaj funkcję replaceAll:

Jakie są przydatne metody JavaScript, które rozszerzają wbudowane obiekty?

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

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

EDYTOWAĆ: Wyjaśnienie dotyczące wymianyWszystkie dostępne

Metoda "replaceAll" jest dodawana do prototypu String. Oznacza to, że będzie on dostępny dla wszystkich obiektów / literałów łańcuchowych.

Na przykład.

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

35
2017-07-17 17:55



Czy możesz przepisać funkcję replaceAll () tam, gdzie nie używasz prototypu? - Click Upvote
@Click Upvote .... używasz prototypu, jest to część wszystkich obiektów JS. Myślę, że myślisz o prototype.js w bibliotece JS. - seth
seth, mała korekta. Jeśli dodasz metodę do prototypu, będzie ona dostępna dla wszystkich obiektów tego typu. Metoda replceAll została dodana do prototypu String i powinna działać dla wszystkich obiektów typu string. - SolutionYogi
@solutionyogi - Tak, użyłem wcześniej prototypu (poprawnie). Właśnie zwróciłem się do komentarza OP na temat "nieużywania prototypu", co moim zdaniem oznaczało prototyp.js (być może niepoprawnie?). Powinienem był powiedzieć "prototyp", ponieważ próbowałem powiedzieć, że obiekty JavaScript mają prototyp. W związku z tym PO już "korzystał z prototypu", chociaż w sposób "pośredni". Pośrednie może być niewłaściwym terminem do użycia tutaj, ale jestem zmęczony, więc mea culpa. - seth