108 Stimmen

Ersetzen Sie eine Regex-Capture-Gruppe durch Großbuchstaben in JavaScript

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

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

Könnten Sie mir bitte erklären, was an diesem Code falsch ist?

1 Stimmen

@Erik entferne keine Komponente einer Frage. Ich möchte auch wissen, warum mein Code nicht funktioniert.

2 Stimmen

Evan, ich dachte, ich wäre respektvoll gegenüber deiner Frage. Ich habe nur Dinge entfernt, die unnötig schienen. Da du den Code angegeben hast, den du ausprobiert hast, und es offensichtlich nicht funktioniert hat, wussten die Leute implizit, dass du eine Erklärung brauchst, warum, ohne dass du es sagen musstest (und ungeschickt). Ich versuche nur zu helfen! :)

1 Stimmen

Evan, ist das besser? Ich will nicht nerven. Wenn du erneut zurückrollst, werde ich nicht nochmal bearbeiten, aber könntest du zumindest die Titel- und Tag-Änderungen beibehalten?

193voto

ChaosPandion Punkte 75527

Sie können einer 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 eine Zeichenfolge an die replace-Funktion. Da Sie die spezielle replace-Syntax verwenden ($N holt die N. Erfassung), geben Sie einfach den gleichen Wert weiter. Das toUpperCase täuscht tatsächlich, weil Sie nur die Ersetzungszeichenfolge in Großbuchstaben umwandeln (was irgendwie sinnlos ist, da die Zeichen $ und 1 keine Großschreibung haben und der Rückgabewert trotzdem "$1" sein wird).

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

Glauben Sie es oder nicht, die Semantik dieses Ausdrucks ist genau die gleiche.

0 Stimmen

@Evan Carroll: Bitte sieh dir meine Antwort an.

4 Stimmen

Ah, ich verstehe, was du meinst, ich schreibe "\$1" in Großbuchstaben. Nicht das Ergebnis des Voodoo, das replace machen wird, das anscheinend $1 für die erste Erfassungsgruppe ersetzt.

0 Stimmen

@EvanCarroll für eine ausführliche Erklärung, warum Ihr anfänglicher Code nicht funktioniert hat und wie man es zum Laufen bringt, sehen Sie meine Antwort unten.

20voto

Bernhard Wagner Punkte 1465

Warum recherchieren wir nicht einfach die Definition?

Wenn wir schreiben:

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

könnten wir genauso gut sagen:

a.replace('f','F')

Schlimmer noch, ich vermute, dass niemand bemerkt hat, dass ihre Beispiele nur funktioniert haben, weil sie das gesamte Regex mit Klammern erfasst haben. Wenn Sie sich die Definition anschauen, wird der erste Parameter, der an die replacer-Funktion übergeben wird, tatsächlich das gesamte übereinstimmende Muster sein und nicht das Muster, das Sie mit Klammern erfasst haben:

function replacer(match, p1, p2, p3, offset, string)

Wenn Sie die Pfeilfunktion verwenden möchten:

a.replace(/xxx(yyy)zzz/, (match, p1) => p1.toUpperCase()

1 Stimmen

IMHO dies ist die einfachste und eleganteste Lösung.

1 Stimmen

Dies ist die einzige richtige Antwort, die erklärt, welches Argument was ist und wie die Übergabe an die Funktion tatsächlich funktioniert, da es nicht so ist, dass `(f)` ein Funktionsaufruf ist... und erklärt, wie man mit komplexeren Regexes und mehreren Erfassungsgruppen arbeitet :-)

19voto

Joshua Piccari Punkte 295

Ich weiß, ich bin spät dran, aber hier ist eine kürzere Methode, die mehr in Richtung Ihrer ersten Versuche geht.

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

Also, wo haben Sie sich geirrt und was ist dieser 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, anstatt eine Referenz auf eine Funktion zu übergeben

Lösung 1

Das ist leicht genug zu lösen. Einfach das Entfernen der Parameter und Klammern gibt uns eine Referenz anstelle der Ausführung der Funktion.

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

Problem 2

Wenn Sie den Code jetzt ausführen, erhalten Sie einen Fehler, der besagt, dass undefined keine Funktion ist und daher nicht aufgerufen werden kann. Dies liegt daran, dass String.prototype.toUpperCase.apply tatsächlich eine Referenz auf Function.prototype.apply() über die prototypische Vererbung von JavaScript ist. Also, was wir tatsächlich tun, sieht eher so aus

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

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

Lösung 2

Indem wir Function.prototype.bind() verwenden, können wir eine Kopie von Function.prototype.call mit seinem Kontext erstellen, der speziell auf String.prototype.toUpperCase festgelegt ist. Jetzt haben wir 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. Jedoch erwartet Function.prototype.apply(), dass der zweite Parameter ein Array ist, erhält aber stattdessen entweder einen String oder eine Zahl (je nachdem, ob Sie Erfassungsgruppen verwenden oder nicht). Dies würde einen Fehler in der Liste der ungültigen Argumente verursachen.

Lösung 3

Zum Glück können wir einfach Function.prototype.call() (das eine beliebige Anzahl von Argumenten akzeptiert, von denen keines Typbeschränkungen hat) für Function.prototype.apply() substituieren. Wir sind jetzt bei funktionierendem Code angekommen!

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

Bytes sparen!

Niemand möchte 'prototype' mehrmals eingeben. Stattdessen nutzen wir die Tatsache, dass wir Objekte haben, die die gleichen Methoden über die Vererbung referenzieren. Der String-Konstruktor, als Funktion, erbt von der Prototype von Function. Das bedeutet, dass wir String.call für Function.prototype.call substituieren können (eigentlich können wir Date.call verwenden, um noch mehr Bytes zu sparen, aber das ist weniger semantisch).

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

5 Stimmen

Du hast 8 Zeichen gespeichert, wobei der Code auf eine Weise verdeckt wurde, die eine Erklärung auf einer Seite erfordert, anstelle der offensichtlicheren Lösung. Ich bin nicht überzeugt, dass dies ein Gewinn ist.

1 Stimmen

Intellektuell gesehen ist dies eine großartige Lösung, da sie etwas über JavaScript-Funktionen offenlegt/lehrt. Aber ich stimme Lawrence zu, dass es in der Praxis zu dunkel ist, um tatsächlich verwendet zu werden. Trotzdem cool.

1 Stimmen

Ja, ich möchte keinen Code wie diesen in Produktion sehen, aber es hat wirklich Spaß gemacht, als ich selbst herausfand, dass man das in JS tun kann :D

12voto

CallMeLaNN Punkte 7909

Alter Beitrag, aber es lohnt sich, die Antwort von @ChaosPandion für andere Anwendungsfälle mit einem strengeren RegEx zu erweitern. Z.B. sicherstellen, dass das (f) oder die Erfassungsgruppe mit einem spezifischen Format /z(f)oo/ umgeben ist:

> a="foobazfoobar"
'foobazfoobar'
> a.replace(/z(f)oo/, function($0,$1) {return $0.replace($1, $1.toUpperCase());})
'foobazFoobar'
// Verbessern Sie den RegEx, so dass `(f)` nur ersetzt wird, wenn es mit einem Punkt oder Zeilenumbruch usw. beginnt.

Ich möchte nur betonen, dass die beiden Parameter der function das Finden eines spezifischen Formats und das Ersetzen einer Erfassungsgruppe innerhalb des Formats ermöglichen.

0 Stimmen

Vielen Dank! Die vorherigen Beiträge scheinen das Problem warum das Code des OP nicht funktioniert hat beantwortet zu haben, während sie völlig übersprungen haben, was für mich das eigentliche Anliegen zu sein schien - das Ersetzen einer Übereinstimmungsgruppe!

0 Stimmen

Ich glaube, du hast einen Fehler in deiner Ersetzungsfunktion, aber überprüfe mich darauf. Ich denke, es sollte return $0.replace($0, $1.toUpperCase()) sein, wobei $0 das erste Argument ist.

0 Stimmen

Es war ein einfacher String-zu-String-Ersatz. Also ist f zu F korrekt.

3voto

Kamil Kiełczewski Punkte 69048

LÖSUNG

a.replace(/(f)/,(m,g)=>g.toUpperCase())  

Um alle Gruppenvorkommen zu ersetzen, verwenden Sie den regulären Ausdruck /(f)/g. Das Problem in Ihrem Code: String.prototype.toUpperCase.apply("$1") und "$1".toUpperCase() ergibt "$1" (probieren Sie es selbst in der Konsole aus) - es ändert also nichts und tatsächlich rufen Sie zweimal a.replace( /(f)/, "$1") auf (was auch nichts ändert).

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

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

0 Stimmen

Was sind m und g? Ich schätze, dass g Gruppe ist?

1 Stimmen

@mikemaccana m bedeutet übereinstimmender Teilstring - es ist ein Argument für die standardisierte Ersetzungsfunktion

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X