Regex: Alle Zahlen aus String filtern
Timo
- php
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
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
4455Ein 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:
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
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
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
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";
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
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";
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
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";
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