Erstellen Sie RegExps im laufenden Betrieb mithilfe von Zeichenfolgenvariablen

138

Angenommen, ich wollte Folgendes wiederverwendbar machen:

function replace_foo(target, replacement) {
   return target.replace("string_to_replace",replacement);
}

Ich könnte so etwas tun:

function replace_foo(target, string_to_replace, replacement) {
   return target.replace(string_to_replace,replacement);
}

Mit String-Literalen ist dies einfach genug. Aber was ist, wenn ich mit dem regulären Ausdruck etwas kniffliger werden möchte? Angenommen, ich möchte alles ersetzen, aber string_to_replace . Instinktiv würde ich versuchen, das oben Gesagte zu erweitern, indem ich etwas tue wie:

function replace_foo(target, string_to_replace, replacement) {
   return target.replace(/^string_to_replace/,replacement);
}

Das scheint nicht zu funktionieren. Ich vermute, dass es sich string_to_replaceeher um ein String-Literal als um eine Variable handelt, die einen String darstellt. Ist es möglich, JavaScript-Regexes im laufenden Betrieb mithilfe von Zeichenfolgenvariablen zu erstellen? So etwas wäre toll, wenn es überhaupt möglich wäre:

function replace_foo(target, string_to_replace, replacement) {
   var regex = "/^" + string_to_replace + "/";
   return target.replace(regex,replacement);
}
buley
quelle

Antworten:

215

Es gibt , new RegExp(string, flags)wo flagssind goder i. So

'GODzilla'.replace( new RegExp('god', 'i'), '' )

bewertet zu

zilla
meder omuraliev
quelle
31
/Lassen Sie auch bei Verwendung dieses Formulars die Regex-Trennzeichen weg.
CDhowie
111

Mit String-Literalen ist dies einfach genug.

Nicht wirklich! Das Beispiel ersetzt nur das erste Auftreten von string_to_replace. In der Regel möchten Sie alle Vorkommen ersetzen. In diesem Fall müssen Sie die Zeichenfolge in eine globale ( /.../g) RegExp konvertieren . Sie können dies mit dem new RegExpKonstruktor aus einer Zeichenfolge heraus tun :

new RegExp(string_to_replace, 'g')

Das Problem dabei ist, dass sich alle Regex-Sonderzeichen im Zeichenfolgenliteral auf ihre besondere Weise verhalten, anstatt normale Zeichen zu sein. Sie müssten ihnen einen Backslash-Escape geben, um das zu beheben. Leider gibt es keine integrierte Funktion, um dies für Sie zu tun. Hier ist eine, die Sie verwenden können:

function escapeRegExp(s) {
    return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
}

Beachten Sie auch, dass bei Verwendung von RegExp in replace()die Ersatzzeichenfolge jetzt auch ein Sonderzeichen hat $. Dies muss auch vermieden werden, wenn Sie ein Literal $in Ihrem Ersatztext haben möchten !

function escapeSubstitute(s) {
    return s.replace(/\$/g, '$$$$');
}

(Vier $s, weil das selbst eine Ersatzzeichenfolge ist - argh!)

Jetzt können Sie das globale Ersetzen von Zeichenfolgen mit RegExp implementieren:

function replace_foo(target, string_to_replace, replacement) {
    var relit= escapeRegExp(string_to_replace);
    var sub= escapeSubstitute(replacement);
    var re= new RegExp(relit, 'g');
    return target.replace(re, sub);
}

Was für ein Schmerz. Glücklicherweise gibt es einen schnelleren Weg, wenn Sie nur eine gerade Saite ohne zusätzliche Teile von Regex ersetzen möchten:

s.split(string_to_replace).join(replacement)

...und das ist alles. Dies ist eine allgemein verständliche Redewendung.

Angenommen, ich möchte alles außer string_to_replace ersetzen

Was bedeutet das, dass Sie alle Textabschnitte ersetzen möchten, die nicht an einer Übereinstimmung mit der Zeichenfolge teilnehmen? Ein Ersatz mit ^sicherlich nicht, denn dies ^bedeutet ein Zeichenfolgenstart-Token, keine Negation. ^ist nur eine Negation in []Zeichengruppen. Es gibt auch negative Lookaheads (?!...), aber es gibt Probleme damit in JScript, daher sollten Sie dies generell vermeiden.

Sie können versuchen, "alles bis zu" der Zeichenfolge abzugleichen und mithilfe einer Funktion alle leeren Abschnitte zwischen übereinstimmenden Zeichenfolgen zu verwerfen:

var re= new RegExp('(.*)($|'+escapeRegExp(string_to_find)+')')
return target.replace(re, function(match) {
    return match[1]===''? match[2] : replacement+match[2];
});

Auch hier könnte eine Aufteilung einfacher sein:

var parts= target.split(string_to_match);
for (var i= parts.length; i-->0;)
    if (parts[i]!=='')
        parts[i]= replacement;
return parts.join(string_to_match);
Bobince
quelle
10

Wie die anderen gesagt haben, verwenden new RegExp(pattern, flags)Sie dies. Es ist erwähnenswert, dass Sie String-Literale an diesen Konstruktor übergeben, sodass jeder Backslash maskiert werden muss. Wenn Sie beispielsweise möchten, dass Ihr Regex mit einem Backslash übereinstimmt, müssen Sie dies sagen new RegExp('\\\\'), während das Regex-Literal nur sein muss /\\/. Abhängig davon, wie Sie dies verwenden möchten, sollten Sie vorsichtig sein, Benutzereingaben an eine solche Funktion zu übergeben, ohne eine angemessene Vorverarbeitung (ohne Sonderzeichen usw.). Ohne dies können Ihre Benutzer einige sehr unerwartete Ergebnisse erzielen.

Kent
quelle
3
Diese Antwort ist zwar nicht die detaillierteste, erwähnt jedoch ein entscheidendes Detail, an dem ich gerade eine Stunde lang festgehalten habe: Entkomme jeglichen speziellen Sequenzen. Zum Beispiel habe ich nach einem Wort gesucht, das mit einem bestimmten Begriff beginnt, also würde ich einen regulären Ausdruck benötigen /\b[term]\B/, aber wenn ich ihn konstruiere, muss ich ihn aufrufen new RegExp("\\b"+ term + "\\B"). Klein , aber wichtiger Unterschied, und hart , da mit ihm zu erkennen als ein regulären Ausdruck direkt funktioniert wie erwartet.
Byson
0

Ich denke, ich habe ein sehr gutes Beispiel für das Hervorheben von Text in einer Zeichenfolge (es wird nicht das Register angezeigt, sondern das Register hervorgehoben).

function getHighlightedText(basicString, filterString) {

    if ((basicString === "") || (basicString === null) || (filterString === "") || (filterString === null)) return basicString;

    return basicString.replace(new RegExp(filterString.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\\\$&'), 'gi'),
        function(match)
            {return "<mark>"+match+"</mark>"});

}

http://jsfiddle.net/cdbzL/1258/

Zhurov Konstantin
quelle
0

Eine wirklich einfache Lösung hierfür ist:

function replace(target, string_to_replace, replacement) {
  return target.split(string_to_replace).join(replacement);
}

Regexes werden überhaupt nicht benötigt

Es scheint auch das schnellste in modernen Browsern zu sein: https://jsperf.com/replace-vs-split-join-vs-replaceall

Jack Allan
quelle