webentwicklung-frage-antwort-db.com.de

Ersetzen Sie eine Regex-Erfassungsgruppe durch Großbuchstaben in Javascript

Ich möchte wissen, wie Sie eine Erfassungsgruppe in JavaScript durch Großbuchstaben ersetzen. Hier ist eine vereinfachte Version dessen, was ich bisher ausprobiert habe und das nicht funktioniert:

> a="foobar"
'foobar'
> a.replace( /(f)/, "$1".toUpperCase() )
'foobar'
> a.replace( /(f)/, String.prototype.toUpperCase.apply("$1") )
'foobar'

Würden Sie erklären, was mit diesem Code falsch ist?

60
Evan Carroll

Sie können eine Funktion an replace übergeben.

var r = a.replace(/(f)/, function(v) { return v.toUpperCase(); });

Erklärung

a.replace( /(f)/, "$1".toUpperCase())

In diesem Beispiel übergeben Sie einen String an die Ersetzungsfunktion. Da Sie die spezielle Ersetzungssyntax ($ N bezieht sich auf das N-te Capture)} verwendet, geben Sie einfach den gleichen Wert an. Die toUpperCase täuscht tatsächlich, weil Sie nur die Ersetzungszeichenfolge in Großbuchstaben (was etwas sinnlos ist, weil die $- und ein 1-Zeichen keine Großbuchstaben enthalten, ausgeben. Der Rückgabewert bleibt also "$1")}.

a.replace( /(f)/, String.prototype.toUpperCase.apply("$1"))

Ob Sie es glauben oder nicht, die Semantik dieses Ausdrucks stimmt genau überein.

131
ChaosPandion

Ich weiß, dass ich zu spät zur Party komme, aber hier ist eine kürzere Methode, die eher Ihren ersten Versuchen entspricht.

a.replace('f', String.call.bind(a.toUpperCase));

Also, wo bist du falsch gegangen und was ist das neue Voodoo?

Problem 1

Wie bereits erwähnt, haben Sie versucht, die Ergebnisse einer aufgerufenen Methode als zweiten Parameter von String.prototype.replace () zu übergeben, wenn Sie stattdessen einen Verweis auf eine Funktion übergeben müssen

Lösung 1

Das ist leicht zu lösen. Durch einfaches Entfernen der Parameter und Klammern erhalten Sie eine Referenz, anstatt die Funktion auszuführen.

a.replace('f', String.prototype.toUpperCase.apply)

Problem 2

Wenn Sie versuchen, den Code jetzt auszuführen, erhalten Sie die Fehlermeldung, dass undefined keine Funktion ist und daher nicht aufgerufen werden kann. Dies liegt daran, dass String.prototype.toUpperCase.apply tatsächlich über die prototypische Vererbung von JavaScript ein Verweis auf Function.prototype.apply () ist. Das, was wir tatsächlich tun, sieht also eher so aus

a.replace('f', Function.prototype.apply)

Welches ist offensichtlich nicht das, was wir beabsichtigt haben. Woher weiß es, Function.prototype.apply () auf String.prototype.toUpperCase () auszuführen?

Lösung 2

Mit Function.prototype.bind () können wir eine Kopie von Function.prototype.call erstellen, deren Kontext speziell auf String.prototype.toUpperCase festgelegt ist. Wir haben jetzt folgendes

a.replace('f', Function.prototype.apply.bind(String.prototype.toUpperCase))

Problem 3

Das letzte Problem ist, dass String.prototype.replace () mehrere Argumente an seine Ersetzungsfunktion übergibt. Function.prototype.apply () } erwartet jedoch, dass der zweite Parameter ein Array ist, sondern erhält stattdessen entweder eine Zeichenfolge oder eine Zahl (je nachdem, ob Sie Erfassungsgruppen verwenden oder nicht). Dies würde einen ungültigen Argumentlistenfehler verursachen.

Lösung 3

Glücklicherweise können wir in Function.prototype.call () (das eine beliebige Anzahl von Argumenten akzeptiert, von denen keines mit Typeinschränkungen versehen ist) durch Function.prototype.apply () ersetzen. Wir sind jetzt beim Arbeitscode angekommen!

a.replace(/f/, Function.prototype.call.bind(String.prototype.toUpperCase))

Bytes verschütten

Niemand möchte mehrere Male prototype eingeben. Stattdessen nutzen wir die Tatsache, dass Objekte über Vererbung auf dieselben Methoden verweisen. Der String-Konstruktor erbt als Funktion vom Prototyp von Function. Dies bedeutet, dass wir in String.call Function.prototype.call ersetzen können (eigentlich können wir Date.call verwenden, um noch mehr Bytes zu speichern, aber das ist weniger semantisch).

Wir können auch unsere Variable 'a' nutzen, da ihr Prototyp eine Referenz auf String.prototype.toUpperCase enthält, die wir mit a.toUpperCase austauschen können. Es ist die Kombination der drei oben genannten Lösungen und dieser Byte-Einsparungsmaßnahmen, mit der wir den Code oben in diesem Beitrag erhalten.

11
Joshua Piccari

Alte Post, aber es lohnt sich, die @ChaosPandion-Antwort für andere Anwendungsfälle mit eingeschränktem RegEx zu erweitern. Z.B. Stellen Sie sicher, dass die (f)- oder Capturing-Gruppe mit einem bestimmten Format umgeben ist /z(f)oo/:

> a="foobazfoobar"
'foobazfoobar'
> a.replace(/z(f)oo/, function($0,$1) {return $0.replace($1, $1.toUpperCase());})
'foobazFoobar'
// Improve the RegEx so `(f)` will only get replaced when it begins with a dot or new line, etc.

Ich möchte nur die beiden Parameter von function hervorheben, um ein bestimmtes Format zu finden und eine Erfassungsgruppe innerhalb des Formats zu ersetzen.

5
CallMeLaNN

LÖSUNG

a.replace(/(f)/,x=>x.toUpperCase())  

verwenden Sie /(f)/g regexp, um alle grup-Vorkommen zu ersetzen. Das Problem in Ihrem Code: String.prototype.toUpperCase.apply("$1") und "$1".toUpperCase() gibt "$1" (versuchen Sie es selbst in der Konsole) - es ändert also nichts und in der Tat rufen Sie zweimal a.replace( /(f)/, "$1") (was auch nichts ändert). 

let a= "foobar";
let b= a.replace(/(f)/,x=>x.toUpperCase());
let c= a.replace(/(o)/g,x=>x.toUpperCase());

console.log("/(f)/ ", b);
console.log("/(o)/g", c);

1

Dazu ein Wörterbuch (Objekt, in diesem Fall eine Map) von Eigenschaften, Werten und die Verwendung von .bind() wie unter Antworten beschrieben

const regex = /([A-z0-9]+)/;
const dictionary = new Map([["hello", 123]]); 
let str = "hello";
str = str.replace(regex, dictionary.get.bind(dictionary));

console.log(str);

Verwenden eines einfachen JavaScript-Objekts und mit einer definierten Funktion, um den übereinstimmenden Eigenschaftswert des Objekts oder den ursprünglichen String abzurufen, wenn keine Übereinstimmung gefunden wird

const regex = /([A-z0-9]+)/;
const dictionary = {
  "hello": 123,
  [Symbol("dictionary")](prop) {
    return this[prop] || prop
  }
};
let str = "hello";
str = str.replace(regex, dictionary[Object.getOwnPropertySymbols(dictionary)[0]].bind(dictionary));

console.log(str);

0
guest271314