Timo: Regex: Alle Zahlen aus String filtern

Hi,

wie kann ich alle echten Zahlenwerte aus einem String filtern, auch wenn darunter Trennzeichen sind.

zb.
str = 'id=xyz><d difopfwf9 <b>555</b> kewffoop ew43534 22.9877 fd<sdr ^ 4ffkp <p>reew 33p efjfi 189,4348 xx</b>2001<b>xx<html>r fei/jefw 8)( ewfd9e730ß 0,0044889  00.0044889 fdl 12.388.444,24 r4rßß 0 <b>44-55</b> rkfr0<tt>dfrfwrigp';

*Der String ist zum testen absichtlich so chaotisch

Das Ergebnis sollte liefern:
555
229877
189.4348
0.0044889
0.0044889
12388444.24
0
4455

Ein Zahl soll richtig sein, wenn sie nicht direkt an ein anderes Zeichen angrenzt mit Ausnahme von typischen Trennern wie "," und "." und mit Ausnahme "innerhalb" Tag-Klammern. <b> 555</b> ist also ein Zahl. </b>333<b> wäre keine. <b>44-55</b> ist eine Zahl, dfdff 44-55 fdfd auch,
rtr44-55fdgfdg alerdings nicht. Auch sollte das Ergebnis in diesem Fall die Zahl ohne Trenner also 4455 zurückliefern. Oder aus 12.388.444,24 sollte 12388444.24 werden.

Ich versuche mich da jetzt schon seit 2 Tagen dran und habe leider auch kaum Ahnung von Regex. Meine Vorgehensweise erzeugt endlos lange Ausdrücke die immer abwechslend nach Buchstaben und Zahlen sucht aber so komme ich nicht weiter: id=xyz(\d+)(\D+)(\d+).*.....usw

Ist mein Vorhaben uberhaupt realistisch mit Regex zu lösen?

Timo

  1. str = 'id=xyz><d difopfwf9 <b>555</b> kewffoop ew43534 22.9877 fd<sdr ^ 4ffkp <p>reew 33p efjfi 189,4348 xx</b>2001<b>xx<html>r fei/jefw 8)( ewfd9e730ß 0,0044889  00.0044889 fdl 12.388.444,24 r4rßß 0 <b>44-55</b> rkfr0<tt>dfrfwrigp';

    *Der String ist zum testen absichtlich so chaotisch

    Das Ergebnis sollte liefern:
    555
    229877

    warum nicht 22.9877

    189.4348

    du liest 189,4348 als 189.4348?
    Ein . ist also kein Dezimalpunkt, sondern ?
    Ein , wird als Dezimalpunkt interpretiert.

    0.0044889

    Wie obiges Beispiel

    0.0044889

    Inkonsistenz zu obigem

    12388444.24
    0
    4455

    Ein Zahl soll richtig sein, wenn sie nicht direkt an ein anderes Zeichen angrenzt mit Ausnahme von typischen Trennern wie "," und "." und mit Ausnahme "innerhalb" Tag-Klammern. <b> 555</b> ist also ein Zahl. </b>333<b> wäre keine. <b>44-55</b> ist eine Zahl, dfdff 44-55 fdfd auch,
    rtr44-55fdgfdg alerdings nicht. Auch sollte das Ergebnis in diesem Fall die Zahl ohne Trenner also 4455 zurückliefern. Oder aus 12.388.444,24 sollte 12388444.24 werden.
    Ich versuche mich da jetzt schon seit 2 Tagen dran und habe leider auch kaum Ahnung von Regex. Meine Vorgehensweise erzeugt endlos lange Ausdrücke die immer abwechslend nach Buchstaben und Zahlen sucht aber so komme ich nicht weiter: id=xyz(\d+)(\D+)(\d+).*.....usw
    Ist mein Vorhaben uberhaupt realistisch mit Regex zu lösen?

    Das kommt darauf an, ob deine Daten sich an irgend eine kanonische Definition von "Zahl" halten.

    Ein Geschmuddel wird nirgendwo hinführen.

    Ein Ansatz besteht darin, dass man kanonische Teile definiert.

    In Perl:

    de und en bezeichnet die Komma/Tausender Schreibweise

    my $integer_de = qr/\d+(?:'\d{3})*/;
                 # 1 12 123 1'234 12'345 123'456 1'234'567

    my $signed_integer_de = qr/[+-]?$integer_de/;
                 # -1 12 123 +1'234 12'345 -123'456 +1'234'567

    my $float_komma_de = qr /.(?:(?:\d{3}')+\d{1,2}|\d+)/;
                 # .1 .12 .123 .1234 .123'4 .123'456'78

    my $signed_float_de = qr/
                (?:
                  $signed_integer_de
                  $float_komma_de
                |
                  $signed_integer_de
                )/x;
                # +0.1  -3.12  -12  123'456.589'01

    usw...

    mfg Beat

    --
    Woran ich arbeite:
    X-Torah
    ><o(((°>      ><o(((°>
       <°)))o><                      ><o(((°>o
    1. Hier ist ein Fehler
      Deutsch verwendet das Komma als Nachkomma Separator.
      http://de.wikipedia.org/wiki/Dezimaltrennzeichen
      Die Gruppierung in verschiedene Tausenderblöcke ist heikler
      Space und . sind in de-Länder üblich, aber auch '
      in en-Länder sind es vorwiegend das Komma oder auch '
      Perl erlaubt und erkennt den Underscore im Programm
      123_456 = 123456

      my $integer_de = qr/\d+(?:'\d{3})*/;
                   # 1 12 123 1'234 12'345 123'456 1'234'567

      Am Besten konsultiert man länderspezifische Normen.

      my $integer_deISO123456 = qr/.../;

      oder Handbücher
      my $integer_perl5 = qr/.../

      Wichtiger war mir, zu zeigen, dass REs modular zusammengebaut werden sollten.

      mfg Beat

      --
      Woran ich arbeite:
      X-Torah
         <°)))o><                      ><o(((°>o
      1. Hi,

        Deutsch verwendet das Komma als Nachkomma Separator.

        ja genau es ging mir tatsächlich um länderübergreifende Resultate.

        Auch dein Ausführungen waren sehr umfassend, so weiss ich nun das es doch nicht mal eben ein Klacks ist das gewünschte hinzubekommen. So kann ich also in Ruhe weiter experimentieren ohne zu denken ein Regex-Profi macht das in ein paar Minuten. Das wäre nämlich frustierend.

        Danke
        Timo

  2. echo $begrüßung;

    Ist mein Vorhaben uberhaupt realistisch mit Regex zu lösen?

    Teilweise.

    Ein Zahl soll richtig sein, wenn sie nicht direkt an ein anderes Zeichen angrenzt mit Ausnahme von typischen Trennern wie "," und "." und mit Ausnahme "innerhalb" Tag-Klammern. <b> 555</b> ist also ein Zahl. </b>333<b> wäre keine. <b>44-55</b> ist eine Zahl, dfdff 44-55 fdfd auch, rtr44-55fdgfdg alerdings nicht.

    Etwas zu finden, dem etwas bestimmmtes vorangeht oder nachfolgt oder dies eben nicht macht, ohne dass dieses mit in das Suchergebnis einfließt, macht man mit Assertions. Diese gibt es in den 4 Geschmacksrichtungen Lookahead und Lookbehind, jeweils positiv und negativ. Das Suchmuster der Assertion kann auch selbst wieder ein Muster mit variablem Anteil sein. Du hast ja hier mehrere, die du mit | (oder) getrennt angeben müsstest. Doch in dem Punkt sind Lookbehind Assertions eingeschränkt. Die Alternativen dürfen nur gleiche statische Länge aufweisen. Vielleicht geht es aber, den kompletten Ausdruck mit den Alternativen komplett zu klammern, so dass er für die Assertion wie einer aussieht.

    Da manche umschließende Muster paarweise auftreten, wirst du vermutlich nicht umhinkommen, diese mit extra abgesetzten preg_match_all() zu suchen, denn sonst fändest du bei Alternativen in den Assertions Dinge wie: dfdff 4711</b>

    Auch sollte das Ergebnis in diesem Fall die Zahl ohne Trenner also 4455 zurückliefern. Oder aus 12.388.444,24 sollte 12388444.24 werden.

    Das geht mit RegExp nicht so einfach. Zeichen auszulassen wüsste ich nur zu realisieren, indem man die gewünschten Zeichen in Subpatterns ermittelt und diese Subpatterns der Ergebnismenge händisch zusammenfügt. Alternativ könnte man normale Stringfunktionen (z.B. strtr()) mit dem Ersetzen von . und - in Nichts und , in . beauftragen. Diesen Vorgang müsstest du extra über die Trennzeichen enthaltende Ergebnismenge laufen lassen.

    Ich versuche mich da jetzt schon seit 2 Tagen dran und habe leider auch kaum Ahnung von Regex. Meine Vorgehensweise erzeugt endlos lange Ausdrücke die immer abwechslend nach Buchstaben und Zahlen sucht aber so komme ich nicht weiter: id=xyz(\d+)(\D+)(\d+).*.....usw

    "Macht mir mal" gibt es hier (normalerweise) nicht. "Die Energie des Verstehens" hilft dir aber, konkrete Fragen zu klären oder Dinge zu erklären. Das Erfolgserlebnis etwas selbst gelöst zu bekommen statt abgeschrieben zu haben, möchte ich dir nicht vorenthalten. Teil das Projekt erst mal in kleine Schritte für die einzelnen Bedingungen und fass diese erst später zusammen. Gleich einen großen Versuch zu starten wird gerade bei "kaum Ahnung" wenig erfolgversprechend sein.

    echo "$verabschiedung $name";

    1. Hi,
      deine ausführungen waren interessant, wenngleich sehr schwere Kost.Assertationen.

      "Macht mir mal" gibt es hier (normalerweise) nicht. "Die Energie des Verstehens" hilft dir aber, konkrete Fragen zu klären oder Dinge zu erklären. Das Erfolgserlebnis etwas selbst gelöst zu bekommen statt abgeschrieben zu haben, möchte ich dir nicht vorenthalten. Teil das Projekt erst mal in kleine Schritte für die einzelnen Bedingungen und fass diese erst später zusammen. Gleich einen großen Versuch zu starten wird gerade bei "kaum Ahnung" wenig erfolgversprechend sein.

      Wo soll ich geschrieben haben "macht mir mal"?

      Es ging mir um das Wissen der Möglichkeit, dass ich nicht umsonst mehr Enrgie darin verschwende. Daher lautete auch meine einzige Frage:

      Ist mein Vorhaben uberhaupt realistisch mit Regex zu lösen?

      Timo

      1. echo $begrüßung;

        Wo soll ich geschrieben haben "macht mir mal"?

        Ich interpretiere das immer in Aussagen wie "Ich habe leider keine / nicht viel Ahnung von ...". Da klingt für mich immer ein "und will es auch nicht sondern nur mein Problem gelöst haben" mit. Wenn es bei dir nicht zutrifft: Entschuldigung. In einem "Das ist mein Problem. Helft ihr mir dabei?" klänge mehr Willen, sich weiterzuentwickeln, an, was das Helfersyndrom bei den Anwortenden mehr anregen dürfte. :-)

        echo "$verabschiedung $name";

  3. Hello,

    Ist mein Vorhaben uberhaupt realistisch mit Regex zu lösen?

    Bestimmt. Und so schwer ist es auch mMn auch nicht.
    Deine Anforderungen sehen sehr nach "Wortgrenze" aus

    Treffer vorhanden

    Array
    (
        [0] => Array
            (
                [0] => 555
                [1] => 22.9877
                [2] => 189,4348
                [3] => 2001
                [4] => 8
                [5] => 0,0044889
                [6] => 00.0044889
                [7] => 12.388.444,24
                [8] => 0
                [9] => 44
                [10] => 55
            )

    )

    So als erster Ansatz:

    <?php   ### zahlen_filtern.php ###

    $str = 'id=xyz><d difopfwf9 <b>555</b> kewffoop ew43534 22.9877 fd<sdr ^ 4ffkp <p>reew 33p efjfi     89,4348 xx</b>2001<b>xx<html>r fei/jefw 8)( ewfd9e730ß 0,0044889  00.0044889 fdl 12.388.444,24 r4rßß 0 <b>44-55</b> rkfr0<tt>dfrfwrigp';

    $pattern = '~\b[0-9,.]+\b~';

    echo (preg_match_all ( $pattern, $str , $_result))?'Treffer vorhanden':'keine Treffer';

    echo "<pre>\r\n";
        echo htmlspecialchars(print_r($_result,1));
        echo "</pre>\r\n";
    ?>

    Die Zusatzbedingugnen kannst Du nun selber einbauen, wolltest Du ja auch, oder? ;-)

    Wie man aber in einem kaputten HTML-String feststellen kann, ob man sich innerhalb oder außerhalb eines inhaltsbewehrten Elementes befindet, dazu fällt mir nicht wirklich etwas ein.

    Liebe Grüße aus Syburg bei Dortmund

    Tom vom Berg

    --
    Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de
    1. echo $begrüßung;

      Deine Anforderungen sehen sehr nach "Wortgrenze" aus

      Das würde ich nicht nehmen, denn das findet gemäß den Anforderungen zu viel. Man braucht jede Menge Ausnahmen, um das überflüssige auszuschließen. Mehrfach durchkämmen mit einfacheren Regeln scheint mit weniger grauen Haaren verbunden zu sein und pflegbareren Code zu erzeugen.

      Wie man aber in einem kaputten HTML-String feststellen kann, ob man sich innerhalb oder außerhalb eines inhaltsbewehrten Elementes befindet, dazu fällt mir nicht wirklich etwas ein.

      Mit sehr tolerantem Parser. Syntax nach klaren Reglen lässt sich stets einfacher parsen als menschliche Fantasie. Mit RegExp allein kann man zwar Muster wie <.+?>zahl</.+?> gut erkennen, und findet dabei auch Tags mit Attributwerten, aber auch ungültiges oder den Suchbedingungen vielleicht nicht entsprechende Vorkommen. Z.B.
      <b><i>zahl</b>
      <p><img …>zahl</p>

      echo "$verabschiedung $name";

    2. Hi Tom,

      Ist mein Vorhaben uberhaupt realistisch mit Regex zu lösen?

      Bestimmt. Und so schwer ist es auch mMn auch nicht.
      Deine Anforderungen sehen sehr nach "Wortgrenze" aus

      Ja eber eben nicht nur leider.

      Treffer vorhanden

      [3] => 2001

      dürfte zb. nicht drin sein.

      $pattern = '~\b[0-9,.]+\b~';

      Danke für deine Mühe.(Ganzer Code) Ein kleines Hilfsmittel, als Hinweis auch an andere hier, könnte der RegexTester sein.

      Mein eigentliches Problem bleibt bestehen aber vielleicht ist mein Beispiel auch einfach nicht praxisgerecht genug.

      Daher: http://www.regex-tester.de/dc_47_de_Google-Treffer-zu-einen-Begriff-anzeigen.html

      * funktioniert natürlich nur mit der deutschen Seite.

      Ein versuch das zu internationalisieren scheitert, warum auch immer.
      http://www.regex-tester.de/uc_489_de.html

      Gruss
      Timo