Nico R.: Wieder mal Frage zu Regex

Guten Abend zusammen,

ich hab einen Regex, der mir alle potenziellen Beträge aus einer Zeichenkette herausfischt: preg_match_all("/[0-9,.]+/", $el, $arr)

Allerdings matcht der auch, wenn nur , oder . in der Zeichenkette steht.

Ich will also erreichen, dass mindestens eine Ziffer vorkommen muss. Das hier führt leider zum gleichen Ergebnis: [0-9{1,},.]+ Sollte nicht eigentlich {1,} oder +genau das gewünschte besorgen?

Schöne Grüße

Nico

  1. Hi,

    ich hab einen Regex, der mir alle potenziellen Beträge aus einer Zeichenkette herausfischt: preg_match_all("/[0-9,.]+/", $el, $arr)

    Allerdings matcht der auch, wenn nur , oder . in der Zeichenkette steht.

    Ich will also erreichen, dass mindestens eine Ziffer vorkommen muss. Das hier führt leider zum gleichen Ergebnis: [0-9{1,},.]+ Sollte nicht eigentlich {1,} oder +genau das gewünschte besorgen?

    Nein, innerhalb einer Zeichenklasse haben Quantifier (* + ? *? +? {n} {n,} {n,m}) keine Wirkung.

    Eine Möglichkeit wären lookarounds (positive lookahead auf eine Ziffer), eine andere, nicht nur einfach alle erlaubten Zeichen aufzulisten, sondern den Regex entsprechend der Struktur einer Zahl aufzubauen (also Vorzeichen optional, Zifferngruppen mit Tausendertrennzeichen, Bruchteiltrenner, Nachkommastellen-Zifferngruppen mit tausendstel-Trennzeichen, ggf. noch Exponent-e, Exponent-Vorzeichen, Exponent-Ziffern ...

    cu,
    Andreas a/k/a MudGuard

    1. Hallo Andreas,

      ja, mir ist auch eingefallen, dass ich das so ähnlich hier schonmal gefragt hatte 😖 Ich versuch mich nochmal dran...

      Gruß Nico

    2. @@MudGuard

      Eine Möglichkeit wären lookarounds (positive lookahead auf eine Ziffer)

      Ich hatte mal einen endlichen Automaten zur Erkennung von Kommazahlen gebaut. Auf den nächsten Slides ist zu sehen, wie er am Nachthimmel zu sehen ist (jetzt schon in der zweiten Nachthälfte, im Winter dann die ganze Nacht). Und hier mit Erklärungen von Matthias Apsel (RIP).

      Wenn es einen endlichen Automaten gibt, dann gibt es auch einen regulären Ausdruck dafür. (Gemeint ist ein regulärer Ausdruck; nicht ein RegExp, was etwas anderes ist – darum ging es ja in der Präsentation.) Wir brauchen keine lookarounds – RegExp mit lookarounds sind keine regulären Ausdrücke.

      eine andere, nicht nur einfach alle erlaubten Zeichen aufzulisten, sondern den Regex entsprechend der Struktur einer Zahl aufzubauen (also Vorzeichen optional, Zifferngruppen mit Tausendertrennzeichen, Bruchteiltrenner, Nachkommastellen-Zifferngruppen mit tausendstel-Trennzeichen, ggf. noch Exponent-e, Exponent-Vorzeichen, Exponent-Ziffern ...

      Ja, so kriegt man das mit einem regulären Ausdruck hin.

      🖖 Live long and prosper

      --
      “In my home, the America I love, the America I've written about, that has been a beacon of hope and liberty for 250 years, is currently in the hands of a corrupt, incompetent and treasonous administration. Tonight, we ask all who believe in democracy and the best of our American spirit, to rise with us, raise your voices against authoritarianism, and let freedom reign.”
      — Bruce Springsteen, Manchester 2025-05-14
      1. Hallo Gunnar Bittersmann,

        das haben wir vor langer Zeit mal gehabt, ja. War spannend. Aber eins ist mir unklar:

        Seite 5: Dass Σ den Zeichenvorrat darstellt und a ein Zeichen daraus, darauf kann man noch kommen. Dass ∅ einen Leerstring darstellt, auch. Im Zweifelsfall liest man den Wikipedia-Artikel über reguläre Ausdrücke. Aber was stellt ε dar?! Aus meiner Sicht gehört diese Zeile da einfach nicht hin.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. @@Rolf B

          das haben wir vor langer Zeit mal gehabt, ja. War spannend. Aber eins ist mir unklar:

          Seite 5: Dass Σ den Zeichenvorrat darstellt und a ein Zeichen daraus, darauf kann man noch kommen. Dass ∅ einen Leerstring darstellt, auch.

          ∅ ist die leere Menge.

          Im Zweifelsfall liest man den Wikipedia-Artikel über reguläre Ausdrücke. Aber was stellt ε dar?!

          ε ist das leere Wort (Leerstring).

          Im Wikipedia-Artikel steht nur was von ∅, nicht von ε. Ich muss mal das in den Referenzen in der Präsentation genannte Buch rauskramen …

          Aus meiner Sicht gehört diese Zeile da einfach nicht hin.

          Möglicherweise sollte man da entweder von ∅ oder von ε sprechen, aber nicht von beidem?

          🖖 Live long and prosper

          --
          “In my home, the America I love, the America I've written about, that has been a beacon of hope and liberty for 250 years, is currently in the hands of a corrupt, incompetent and treasonous administration. Tonight, we ask all who believe in democracy and the best of our American spirit, to rise with us, raise your voices against authoritarianism, and let freedom reign.”
          — Bruce Springsteen, Manchester 2025-05-14
          1. Hallo Gunnar Bittersmann,

            aber nicht von beidem

            Genau. Ansonsten müsstest Du mir bei einem Wort den Unterschied zwischen leerer Menge und leerem String erklären. Denn kenn ich nicht, deshalb hab ich ∅ auch mit Leerstring gleichgesetzt

            Programmiertechnisch wäre das wohl null bzw. undefined vs "" - aber damit beschäftigt sich die theoretische Informatik weniger…

            Buch rauskramen

            Bei mir wäre das "Einführung in die Theoretische Informatik, Teil B". Den Kurz habe ich zwar gut bestanden, den Ordner aber längst dem Studium hinterher geschmissen (Vollzeit arbeiten, 2h pendeln und Teilzeit Info mit NF BWL studieren war mir zu viel).

            Rolf

            --
            sumpsi - posui - obstruxi
  2. Hallo Nico,

    so einfach ist das nicht. In einer Regex bedeutet ˋ[...]ˋ ein Zeichen aus einem bestimmten Zeichenvorrat. Das kann sein:

    • Auflistung von Zeichen
    • Auflistung von Zeichenbereichen.

    ˋ[abcxyz]ˋ bedeutet: Matche ein Zeichen, das a,b,c,x,y oder z ist
    ˋ[a-z]ˋ bedeutet: Matche ein Zeichen aus dem Bereich der Zeichen von a bis z. Das geht nach ihrem Zeichencode (ursprünglich ASCII, heute Unicode),

    Das kann man auch kombinieren: ˋ[a-z0-9]ˋ matcht ein Zeichen, das im Bereich a-z oder 0-9 ist.

    Eine solche [] Gruppe kann mit einem Multiplikator versehen werden. ˋ[a-z]*ˋ oder ˋ[a-z]{0,}ˋ matchen beliebig viele Zeichen a-z, ˋ[a-z]+ˋ oder ˋ[a-z]{1,}ˋ matchen ein oder mehr Zeichen, ˋ[a-z]?ˋ oder ˋ[a-z]{0,1}ˋ matchen 0 oder 1 Vorkommen und allgemein matcht ˋ[a-z]{m,n}ˋ m bis n Vorkommen.

    Innerhalb der eckigen Klammern hat ein Multiplikator aber keine Bedeutung, innerhalb der eckigen Klammern hast Du keine Regex-Syntax, nur die Zeichenbereich-Syntax.

    Ich mutmaße, dass man deine Abfrage mit einem Lookahead oder Lookbehind lösen könnte, aber das würde ich für den falschen Ansatz halten. Denn deine Regex ist ohnehin noch zu generisch, sie würde auch "12,345,65" matchen oder "12...45". Absicht? Frage ist auch, warum Du . und , drin hast. Möchtest Du Dezimalpunkt UND Dezimalkomma treffen, oder möchtest Du auch Tausendertrennzeichen erfassen können?

    Wenn wir mal nur vom Dezimalkomma reden, dann brauchst Du eine Regex, die mindestens folgende Schreibweisen versteht - das ist die Pflicht:

    12345 (Nur Ziffern)
    123,456 (Ein und genau ein Dezimalkomma)

    Kür 1:
    123.456 (auch ein und genau ein Dezimalpunkt)

    Kür 2:
    ,543 (die Ziffern vor dem Dezimalkomma wurden weggelassen)

    Kür 3:
    1.234,56 (Tausendertrennzeichen)

    Kür 4:
    1,234.56 (englisch mit Tausenderkomma)
    1'234,56 (schweizerisch)

    Die Küren 1 und 4 zeigen das Hauptproblem: Zahlenformatierung ist Ländersache, und Kür 1 zeigt, wie schnell man sich um einen Faktor 1000 irren kann, wenn man im falschen Land ist. Multinationale Zahlenerkennung gelingt nur mühsam, und wusstest Du schon, dass es nach DIN auch ein Leerzeichen als Tausendertrenner gibt?

    Also - bevor wir Regexe bauen - welche Kür willst Du tanzen und welche nicht?

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf,

      ich konnte es mit Hilfe dieses Beitrags, in dem ihr mir schonmal dazu geholfen hattet, lösen.

      Ziel war es ja (eigentlich wie damals) aus einer Zeichenkette, einen Betrag herauszufischen, der folgendermaßen aussehen darf: 19 19,00 19.00 19,-
      Tausendertrennzeichen gibt es nicht, es handelt sich in der Regel um Beträge unter 100. In Ausnahmefällen kann der Betrag dreistellig sein, aber vierstellig in keinem Fall. Diesen Zweck erfüllt jetzt dieser Regex:

      ([0-9]{0,3}([,.][0-9]{2}|[,][-])?)

      Sofern ich nichts übersehen habe 🤔

      Schönen Abend und besten Dank nochmal

      Nico

      1. Hallo Nico,

        okay, wenn es um Geldbeträge geht, ist die Festlegung auf 2 Nachkommastellen sinnvoll und ",-" ein sinnvoller Zusatz.

        Dürfte passen. Auf https://regex101.com kannst Du deine Regex gegen diversen Input testen.

        Rolf

        --
        sumpsi - posui - obstruxi
      2. @@Nico R.

        ([0-9]{0,3}([,.][0-9]{2}|[,][-])?)

        Sofern ich nichts übersehen habe 🤔

        Doch, hast du. Sogar einiges:

        • Wenn danach Ziffern folgen, erlaubst du Punkt und Komma als Dezimaltrennzeichen. Wenn danach ein Strich folgt, soll nur das Komma als Dezimaltrennzeichen erlaubt sein?

        • Außer dem Fliegenschiss ‚-‘ (U+002D) sollte auch das richtige dafür zu verwendete Zeichen erlaubt sein: der Halbgeviertstrich ‚–‘ (U+2013). Evtl. auch der Geviertstrich, s. Abschnitt Geldbeträge.

        • Dein Ausdruck erlaubt auch den Leerstring; das willst du sicher nicht.

        Zeicheklassen mit nur einem Zeichen machen wenig Sinn, dann kannst du gleich das Zeichen an sich notieren, d.h. ,- anstatt [,][-].

        🖖 Live long and prosper

        --
        “In my home, the America I love, the America I've written about, that has been a beacon of hope and liberty for 250 years, is currently in the hands of a corrupt, incompetent and treasonous administration. Tonight, we ask all who believe in democracy and the best of our American spirit, to rise with us, raise your voices against authoritarianism, and let freedom reign.”
        — Bruce Springsteen, Manchester 2025-05-14
        1. Hallo Gunnar,

          Sofern ich nichts übersehen habe 🤔

          Doch, hast du. Sogar einiges:

          Fällt mir auch gerade auf. Dazu schreibe ich demnächst nochmal...

          • Wenn danach Ziffern folgen, erlaubst du Punkt und Komma als Dezimaltrennzeichen. Wenn danach ein Strich folgt, soll nur das Komma als Dezimaltrennzeichen erlaubt sein?

          Ja, eigentlich schon. Das ist ja in D die übliche Abkürzung bei geraden Geldbeträgen. Ich hätte natürlich mal dazu schreiben sollen, dass es um Geldbeträge geht…

          • Außer dem Fliegenschiss ‚-‘ (U+002D) sollte auch das richtige dafür zu verwendete Zeichen erlaubt sein: der Halbgeviertstrich ‚–‘ (U+2013). Evtl. auch der Geviertstrich, s. Abschnitt Geldbeträge.

          Ach herrje. Guter Hinweis. Ich denke zwar, dass 98 Prozent der Nutzer, wie auch ich, den falschen Strich nutzen, einfach, weil es keine Taste für den Halbgeviertstrich gibt, aber entgegennehmen muss man den natürlich (und am besten gleich noch ein paar Belohnungskonfettis regnen lassen 🎈🎉).

          • Dein Ausdruck erlaubt auch den Leerstring; das willst du sicher nicht.

          An welcher Stelle denn?

          Zeicheklassen mit nur einem Zeichen machen wenig Sinn, dann kannst du gleich das Zeichen an sich notieren, d.h. ,- anstatt [,][-].

          Ah, okay. Hab ich ersetzt.

          Schöne Grüße

          Nico

          1. @@Nico R.

            • Wenn danach Ziffern folgen, erlaubst du Punkt und Komma als Dezimaltrennzeichen. Wenn danach ein Strich folgt, soll nur das Komma als Dezimaltrennzeichen erlaubt sein?

            Ja, eigentlich schon. Das ist ja in D die übliche Abkürzung bei geraden Geldbeträgen. Ich hätte natürlich mal dazu schreiben sollen, dass es um Geldbeträge geht…

            Hast du doch? Aber vielleicht würde wirklich niemand ‚12.–‘ mit Punkt schreiben, während man ‚12.34‘ neben ‚12,34‘ zulassen sollte.

            Ach herrje. Guter Hinweis. Ich denke zwar, dass 98 Prozent der Nutzer, wie auch ich, den falschen Strich nutzen, einfach, weil es keine Taste für den Halbgeviertstrich gibt,

            Auf macOS ganz einfach: [⌥ option][-].

            aber entgegennehmen muss man den natürlich (und am besten gleich noch ein paar Belohnungskonfettis regnen lassen 🎈🎉).

            Hach, ich fühle mich wie die Goldmarie bei Frau Holle.

            • Dein Ausdruck erlaubt auch den Leerstring; das willst du sicher nicht.

            An welcher Stelle denn?

            Vorn [0-9]{0,3} muss keine Ziffer stehen; das Hinterteil ([,.][0-9]{2}|[,][-])? ist optional.

            🖖 Live long and prosper

            --
            “In my home, the America I love, the America I've written about, that has been a beacon of hope and liberty for 250 years, is currently in the hands of a corrupt, incompetent and treasonous administration. Tonight, we ask all who believe in democracy and the best of our American spirit, to rise with us, raise your voices against authoritarianism, and let freedom reign.”
            — Bruce Springsteen, Manchester 2025-05-14
            1. Hallo Gunnar,

              Vorn [0-9]{0,3} muss keine Ziffer stehen; das Hinterteil ([,.][0-9]{2}|[,][-])? ist optional.

              Ja, aber der RegexCoach selektiert nichts, wenn ich einfach nur Leerzeichen eingebe. Beim Test mit .+ werden Leerzeichen selektiert.

              Schöne Grüße

              Nico

          2. Hallo nochmal,

            Sofern ich nichts übersehen habe 🤔

            Doch, hast du. Sogar einiges:

            Fällt mir auch gerade auf. Dazu schreibe ich demnächst nochmal...

            Es gibt noch ein paar Probleme. Hier mal das erste und mein aktueller Regex:

            ([0-9]{0,3}([,.][0-9]{2}|,-|,–)?)

            Ich habe festgestellt, dass Beträge nur selektiert werden, wenn die Zeichenkette direkt mit einer Ziffer beginnt (12,34). Bei test 12,34 schlägt der Regex nicht mehr an. Das kann ich lösen, in dem ich den zweiten Teil nicht mit ? optional mache:

            ([0-9]{0,3}([,.][0-9]{2}|,-|,–))

            Dann wird auch test 12,34 oder test 12,- gefunden. Allerdings nicht mehr 12 oder test 12, da ja der Nachkommateil nicht mehr optional ist.

            Also ist wohl doch der erste Regex der richtige Ansatz? Aber wieso findet er den Betrag nicht an einer beliebigen Stelle? Ich muss doch dafür eigentlich nichts explizit angeben, oder?

            Schöne Grüße

            Nico

            1. Hi,

              Es gibt noch ein paar Probleme. Hier mal das erste und mein aktueller Regex:

              ([0-9]{0,3}([,.][0-9]{2}|,-|,–)?)

              Ich habe festgestellt, dass Beträge nur selektiert werden, wenn die Zeichenkette direkt mit einer Ziffer beginnt (12,34).

              Versuchst Du ein match oder ein find? match setzt implizit ^ und $ an Anfang und Ende ...

              cu,
              Andreas a/k/a MudGuard

              1. Hallo Andreas,

                ich nutze den Ausdruck mit preg_match_all, um alle Beträge im Suchstring einzusammeln. Spielt das für den Ausdruck an sich eine Rolle? Ich habe das Verhalten ja auch im Regex Coach.

                Schöne Grüße

                Nico

              2. Hallo MudGuard,

                von welcher Programmierumgebung sprichst du? PHP?

                Rolf

                --
                sumpsi - posui - obstruxi
                1. Hi,

                  von welcher Programmierumgebung sprichst du? PHP?

                  ist das in PHP anders als in Java? Ich hab schon zu lange nix mehr mit regex in PHP gemacht …

                  cu,
                  Andreas a/k/a MudGuard

                  1. Hallo MudGuard,

                    ja, in PHP gibt's nur match und da ist kein ^$ inkludiert.

                    Testprogramm mit vereinfachtem Muster:

                    $a = "Hallo 123,45 Welt 12,-";
                    $res = [];
                    preg_match_all("/\d+(,\d+)?/", $a, $res);
                    var_dump($res);
                    

                    findet 123,45 und 12. Es liegt also nicht am optionalen Teil, da ist noch was anderes, was wir ggf. hier nicht gezeigt bekommen.

                    Rolf

                    --
                    sumpsi - posui - obstruxi
            2. Hallo Nico R.,

              ich habe das jetzt mal in onlinephp.io probiert.

              • verwende bitte {1,3} statt {0,3}. Oder willst Du unbedingt ",50" erkennen können? Wenn ich {0,3} verwende, erhalte ich eine Menge "Nulltreffer".
              • lass die äußeren Klammern weg, die sind unnötig und erzeugen lediglich ein zweites Subarray im Ergebnis, das mit dem ersten Subarray identisch ist.
              • du kannst [0-9] mit \d abkürzen
              • wenn du außerhalb von [] nach einem Punkt suchen willst, musst Du ihn mit Backslash escapen, weil ein Punkt beliebige Zeichen matcht. Ich habe den Match auf - deshalb etwas geändert.
              $a = "Hallo 321 und 123.45 oder 123,45 Welt 12,- für 12.- Euro";
              $res = [];
              preg_match_all("/\d{1,3}([,.](\d{2}|-))?/", $a, $res);
              var_dump($res);
              

              liefert mir in $res[0] alle 5 Zahlen.

              Rolf

              --
              sumpsi - posui - obstruxi
              1. @@Rolf B

                • lass die äußeren Klammern weg, die sind unnötig und erzeugen lediglich ein zweites Subarray im Ergebnis, das mit dem ersten Subarray identisch ist.

                Und bei den inneren will man sicher auch die Teiltreffer nicht haben. Also für die öffnende Klammer (?: verwenden. (Die schließende bleibt ).)

                🖖 Live long and prosper

                --
                “In my home, the America I love, the America I've written about, that has been a beacon of hope and liberty for 250 years, is currently in the hands of a corrupt, incompetent and treasonous administration. Tonight, we ask all who believe in democracy and the best of our American spirit, to rise with us, raise your voices against authoritarianism, and let freedom reign.”
                — Bruce Springsteen, Manchester 2025-05-14