Ersetzen eines exakten Ausdrucks mit replace

  • Hallo zusammen,


    ich ersetze in einem String Zahlen durch Zahlwörter:


    replace('2', 'zwei')


    Das funktioniert prima, solange keine "12" ersetzt werden muss, denn dann kommt "1zwei" heraus. Ich hab' einige ergoogelte Versuche mit Regex unternommen, um das in den Griff zu kriegen, aber ohne brauchbares Ergebnis.


    Wie macht man es korrekt, dass nur der exakte Ausdruck, in meinem Fall die Ziffer 2 zwischen den Hochkommas, ersetzt wird?


    Danke und Gruß

    Felix

  • Zitat

    Wie macht man es korrekt, dass nur der exakte Ausdruck, in meinem Fall die Ziffer 2 zwischen den Hochkommas, ersetzt wird?

    Meinst Du, die 2 soll nur ersetzt werden, wenn sie allein innerhalb von Text ohne Ziffern steht? Dann müsste es so funktionieren:

    str.replace(/([^\d])2([^\d])/g, '$1zwei$2')

    Die 2 wird nur ersetzt, wenn das Zeichen davor und das Zeichen danach keine Ziffer ist.

  • Die replace Funktion in JavaScript ist sehr mächtig.
    Sie kann statt einem replace String eine Replacer Funktion annehmen.


    mit diesem Trick könntest du alle Zahlen auf einmal ersetzen. so in etwa:

    str.replace(/(\d+)/g, (_, nr) => numberWordMap[nr])

    (numberWordMap ist ein Objekt, das als key die Nummer in Ziffern und als value die Nummer als Wort enthällt.)

  • Sempervivum Das funktioniert sehr gut, vielen Dank! Drei Fragen habe ich:


    1. Wofür stehen "$1" und "$2" beim Ersetzungsausdruck?


    2. Warum funktioniert bei mir zwar document.getElementById('testIt').innerHTML = ('Es sind 3 Fragen.').replace(/([^\d])3([^\d])/g, '$1drei$2');, aber nicht document.getElementById('testIt').innerHTML = ('Es sind 3 Fragen.').str.replace(/([^\d])3([^\d])/g, '$1drei$2');?

    Der Editor sagt, "str.replace" ist für diesen Typ nicht vorhanden.


    3. Das Ganze funktioniert gar nicht, wenn ich es in meinem Musik-Quiz an dieser Stelle benutzen will (Zeile 90): function countQuestions() { countQa.innerHTML = questions.length.toString().replace(/([^\d])3([^\d])/g, '$1drei$2');; }

    Mit "str" wird die ganze Funktion lahmgelegt, ohne "str" wird das "replace" ignoriert.



    AndreasB Das klingt spannend, vielen Dank! Ich werde mich eingehend damit beschäftigen, denn so eine Funktion werde ich garantiert immer wieder brauchen.

  • Zitat

    1. Wofür stehen "$1" und "$2" beim Ersetzungsausdruck?

    Die werden durch die geklammerten Teilausdrücke in der Regex ersetzt, d. h. diese: ([^\d]) bzw. die Zeichen vor und hinter der 2.

    Wenn man diese nicht einbeziehen würde, würden die Zeichen links und rechts von der 2 verschwinden.


    2. Hier ('Es sind 3 Fragen.').str.replace(/([^\d])3([^\d])/g, '$1drei$2'); ist das str vor dem replace überflüssig, denn das geklammerte ist ja schon der String, in dem Du ersetzen willst.


    3. Dieses questions.length.toString() liefert einen Integer, d. h. einen reinen Zahlenwert. Die Regex prüft jedoch ob links und rechts davon ein Zeichen steht, das keine Ziffer ist und das ist ja bei der Länge bzw. einem Integer nicht der Fall.

  • Herzlichen Dank! Bei 3. habe ich aber ein Verständnisproblem.


    Ich habe doch das toString() angefügt, damit ich statt einer Zahl einen String bekomme. Nach meinem Verständnis ist die "3" dann einfach ein Stück Text, oder?


    console.log(typeof questions.length.toString()) meldet, dass ich einen String bekomme. Allerdings stehen rechts und links keine Zeichen, deshalb ist der Regex hier wirkungslos, verstanden. Aber wie fange ich in meinem Fall diese "3" dann so ab, dass aus "13" nicht "1drei" wird?

  • Zitat

    wie fange ich in meinem Fall diese "3" dann so ab, dass aus "13" nicht "1drei" wird?

    In diesem Fall ist die Situation jetzt ein wenig anders: Nur ersetzen wenn keine Zeichen links und rechts sondern die 3 allein steht. Das geht dann so:

    '3'.replace(/^3$/g, 'drei')

    ^ steht dabei für den Anfang und $ für das Ende des Strings.

  • Ich möchte mich ganz herzlich bei allen bedanken!


    Ich habe alle hier gezeigten Varianten ausprobiert, jede hat ihren eigenen Einsatzzweck. Allerdings habe ich ein Problem mit dem Vorschlag aus #3, wie man hier sieht:


    https://jsfiddle.net/wv2exsk4/1/


    Im Script sind ab Zeile 89 die Ersetzungsausdrücke von 2 bis 12 definiert. Ab 13 soll keine Ersetzung mehr stattfinden. Mein Frage-Antwort-Objekt hat 13 Einträge, die 13 ist aber im Ersetzungsobjekt nicht definiert, also kommt es zum Ausgabefehler "undefined".


    Wie bringe ich der Funktion bei, dass sie ab 13 stoppt?

  • Wie bringe ich der Funktion bei, dass sie ab 13 stoppt?

    Einfach im Ausdruck nur die Zahlen 1-12 erlauben: »\b([1-9]|1[0-2])\b«. Alternativ abfragen ob numberWordMap[nr] existiert:

    JavaScript
    1. '13'.replace(/(\d+)/g, (_, nr) => numberWordMap[nr] ? numberWordMap[nr] : nr

    (ob das wirklich korrekt so ist muss jemand beurteilen der in JS besser ist als ich, gibt es evtl. einen ??-Operator wie in PHP?)

  • Failix danke für den Code, jetzt kann man dir präziser helfen.


    Du denkst zu kompliziert.

    Einen Regulären Ausdruck, benötigt man, wenn man ein Muster ersetzen möchte. Ein Muster verwendet man, wenn ein direkter Vergleich zu kompliziert ist, oder wenn man zu viele Ausprägungen hat um alle einzeln zu vergleichen (Dies ist meistens der Fall, wenn man Teile eines kompletten Textes ersetzen möchte).


    In deinem Fall, möchtest du einen fixen Ausdruck (stets eine bestimmte Zahl) durch einen entsprechenden Ausdruck ersetzen. Dafür ist ein Regulärer Ausdruck nicht die Beste Wahl. (Du hast das auch selber gemerkt und für jede Zahl eine eigene replace Anweisung geschrieben).


    Im Grunde benötigst du einen konkreten Vergleich wie ich in #7 schon geschrieben hatte.

    Da du schon eine Map für die Zahlen hast, musst du nur einen Lookup auf diese Map machen:

    JavaScript
    1. const nr = 3;
    2. numberWordMap[3]; // => 'drei'

    das reicht.


    Für einen Fallback kannst du zB || nrverwenden.

  • Ich hatte in #4 einen Link auf den Quelltext in seiner Version nach dem ersten Ersetzungsversuch gepostet, aber der war sicher nicht auffällig genug. Tut mir leid, wenn Ihr deswegen ein wenig rätseln musstet, aber für mich war das alles sehr interessant und lehrreich. RegEx werde ich immer wieder brauchen, und es wird Zeit, dass ich meine rudimentären Kenntnisse darin erweitere.


    Also, noch einmal herzlichen Dank an alle!


    Sempervivum Ich habe Deinen Quelltext ausprobiert, verstehe aber die Funktion nrToWord nicht wirklich. Was bewirkt diese Bedingung?

    JavaScript
    1.   if (numberWordMap[nr]) {
    2. return numberWordMap[nr];
    3. }


    AndreasB Das mit dem konkreten Vergleich habe ich verstanden, den kann man ja auf verschiedene Weisen machen, da würde auch eine Kette aus Replace-Anweisungen wie in #10 ihren Dienst tun.


    Was ich nicht kapiere, sind Lookup und Fallback. Ich bin noch ziemlicher Anfänger in JS, der Begriff Lookup begegnet mir gerade zum ersten Mal. Was ein Fallback ist, weiß ich theoretisch, nicht aber für genanntes Beispiel.


    Weitere Infos sind also sehr, sehr willkommen. :)

  • Mit Lookup ist hier nur gemeint, wenn man nach einer Property in einem Objekt "sucht"

    JavaScript
    1. object[property];


    Mit Fallback ist hier der Wert gemeint, den die Variable haben wird, wenn der Lookup schief geht (wenn der Lookup "undefined" zurück liefert).

    Um das mit den || zu verstehen, schau dir mal Short Circuit Evaluation oder auch Ein deutsches Beispiel an. (Das ist in JS Standard und das sollte man kennen um viele der Beispiele die man so findet zu verstehen)

  • Hallo Failix , dieses:

    Code
    1. if (numberWordMap[nr]) {
    2. return numberWordMap[nr];
    3. }

    soll den Fall abdecken, den Du in #11 angesprochen hast:

    Zitat

    Ab 13 soll keine Ersetzung mehr stattfinden. Mein Frage-Antwort-Objekt hat 13 Einträge, die 13 ist aber im Ersetzungsobjekt nicht definiert, also kommt es zum Ausgabefehler "undefined".

    Nur wenn ein Eintrag in der Liste mit den Wörtern existiert, wird die Funktion mit dem betr. Wort verlassen, wenn nicht, wird die Zahl zurück gegeben.

  • AndreasB Ich kenne den Operator || nur aus ganz simplen Abfragen. Wenn ich mir anschaue, was es hinter den von Dir geposteten Links zu lesen gibt, weiß ich, dass ich nichts weiß ... ?( Deshalb verstehe ich wohl auch Deinen Ansatz aus #14 derzeit überhaupt nicht.


    Sempervivum Vermutet habe ich das schon, kann mir nur nicht so richtig erklären, was die eckigen Klammern bedeuten. Aber, siehe oben ...


    Kein guter Tag heute für mich. Einer von den Tagen, an denen man sich ganz klein und ahnungslos vorkommt und mächtigen Frust deswegen empfindet.


    In meinem Videokurs sind wir in puncto Ersetzen bisher nicht über eine Schlange aus aneinander gehängten replace() hinausgekommen. Der Dozent meinte dazu, dass er später auf eine elegantere Methode eingehen würde – ich bin gespannt, wann es so weit ist. So richtig komme ich mit dem Kurs auch nicht voran, einfach zu wenig Zeit. :(


    Aber immerhin funktioniert mein Quiz jetzt wieder etwas besser dank Eurer Hilfe. Ich wünsche Euch einen schönen Sonntagabend!

  • failix schrieb:

    kann mir nur nicht so richtig erklären, was die eckigen Klammern bedeuten.

    Das wird z. B. hier erklärt:

    https://developer.mozilla.org/…ence/Global_Objects/Array


    failix schrieb:

    Kein guter Tag heute für mich. Einer von den Tagen, an denen man sich ganz klein und ahnungslos vorkommt und mächtigen Frust deswegen empfindet.

    Einer hat mir mal geraten: "Go slow for the beginning." Es ist ein bewährtes Verfahren, Schritt-für-Schritt vorzugehen und den zweiten Schritt nicht vor dem ersten zu tun. Insofern hat das Vorgehen deines Dozenten schon etwas für sich.