PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Probleme mit einem regulären Ausdruck


Monger
2008-06-12, 09:57:14
Servus!
Ich beschäftige mich gerade (relativ frisch) mit regulären Ausdrücken - und stolper gerade über etwas, wo mir einfach derzeit die Vorstellungskraft fehlt. Könnte mir hier vielleicht ein Regex-Guru ein bißchen auf die Sprünge helfen?

Ich habe eine Art Addressformat, das ich erkennen will. Das sieht zum Beispiel so aus: DBB 4, DBX 2.1 , EW 2, E 0.0 ...

Ich hab also erst eine beliebige Zeichenfolge, und dann eine Zahl. Wenn von der Zeichenfolge der letzte Buchstabe entweder B, W oder D ist, darf hinten kein Punkt und keine Zahl mehr folgen, ansonsten ist ein . und 0 bis 7 erlaubt. DBB 3.2 ist also NICHT erlaubt!

Ich hab mir bis jetzt folgenden Ausdruck zusammengezimmert:

\w+?([BWD])?[ ]?(\d+(?(1)|\.[0-7]))

Das Problem ist, dass damit z.B. DBB 4.1 auch ein gültiger Ausdruck ist, weil er dann den DBB 4 Anteil als korrekt annimmt.

Was kann ich denn gegen sowas tun?
Ich könnte natürlich für beide Fälle einen eigenen Ausdruck bauen, und diese dann ver-Odern... aber das muss doch noch besser gehen?!?

HoVa
2008-06-12, 13:59:30
Vielleicht helfen dir bedingte Ausdruecke weiter
http://de.wikipedia.org/wiki/RegulC3%A4rer_Ausdruck#Bedingte_Ausdr.C3.BCcke
aber eben nur in wenigen Sprachen verfuegbar.

Wuerde dir zu einem "traditionellem" If-Else raten.

HoVa
2008-06-12, 14:05:18
\p{L}{0,10}(?<=B|D|W)\s\d(?!\.|\d)

fuer den ersten Fall (BDW --> kein Punkt, kein Digit) habe ich dir Folgendes mal zusammengestellt. Wenn du Fragen hast, stell sie. Wenn du den Ausdruck verstehst, sollte der andere kein Problem mehr darstellen.

Kleiner Erlaeuterung
p\{L} habe ich jetzt mal mit einem Auftreten von 0 bis 10 eingeschraenkt. Kannst du notfalls nach Belieben aendern, aber * (0 bis unendlich) wird in den seltendsten Faellen wirklich gebraucht und verschwendet somit nur Ressourcen.

Kinman
2008-06-12, 14:10:17
http://regexp-evaluator.de/tutorial/einleitung/
Ist eine super Seite, allerdings weiß ich nicht, ob Du mit Deinen Anforderungen hier noch fündig wirst.

mfg Kinman

Monger
2008-06-12, 14:39:22
Da gibt es in der Tat ganz viel was ich nicht verstehe. Nun gut, gehen wir es an...

\p{L}{0,10}

Dazu habe ich jetzt auch keine Dokumentation finden können. Was ist denn \p{L}?
Wenn das gleichbedeutend mit \w{0,10} ist - um Performance brauche ich mir zumindest an der Stelle noch keine Gedanken zu machen. Mir geht es erstmal darum, zu verstehen wie ich sowas erstmal grundsätzlich lösen kann, bevor ich an Performance denke

(?<=B|D|W)\s

Das ist ein Lookbehind fürs Leerzeichen, nicht wahr?

\d(?!\.|\d)

Und das sieht aus wie ein Lookahead. Aber wozu? Ich will doch im Zusammenhang mit BDW eben gerade NICHT, dass Punkt plus Zahl am Ende erlaubt ist.


Mir geht es auch mehr darum zu lernen, wie man denn sowas in den Griff kriegen könnte, wenn ellenlange Veroderungen keine Option sind.

HoVa
2008-06-12, 14:58:46
\p{L} ist ein Buchstabe (nicht case-sensitive) des Unicode.
\p{Lu} waere nur upper-case
\p{Ll} nur lower-case


(?<=B|D|W) erwartet B oder D oder W vor(!) dem darauf folgenden Ausdruck.

\s ist nur ein beliebiger Whitespace (Blank, Tab..), gehoert nicht zu der positiven Lookbehind-Assertion mit dem B,D,W.

Dann folgt eben ein Digit --> \d
und dieser Digit gehoert nicht(!) zu der negativen Lookahead-Assertion. Diese Assertion verbietet, dass ein entweder ein Punkt oder ein Digit nach dem bisherigen Ausdruck folgt.

HoVa
2008-06-12, 15:02:51
ach ich hab dich falsch verstanden. Wenn B,D,W dort steht, darf kein Punkt gefolgt von einem weiteren Digit kommen.

\p{L}{0,10}(?<=B|D|W)\s\d(?!\.\d)

dann einfach das | entfernen.

Monger
2008-06-12, 15:42:24
Sorry, ich versteh immer noch den Ausdruck hier nicht:
\d(?!\.\d)


Er funktioniert tatsächlich - allerdings verstehe ich nicht warum. Was genau bedeutet (?!\.\d) ?

Edit: Okay, der Groschen ist gefallen. Negative Lookahead.
Jetzt verstehe ich auch worauf du hinaus willst. Mal sehen ob ich auf die Weise zum Ziel komme...

RattuS
2008-06-12, 16:40:21
Wuerde dir zu einem "traditionellem" If-Else raten.
Da stimme ich HoVa zu. Die Regex-Funktion ist auf unterster Ebene nichts anderes als Bedingungsmatrizen. Die Übersicht leidet bei beiden Techniken, ob du nun einen schwer leserlichen Formatstring oder eine tief verschachtelte Kette hast.

del_4901
2008-06-12, 18:39:47
Da stimme ich HoVa zu. Die Regex-Funktion ist auf unterster Ebene nichts anderes als Bedingungsmatrizen. Die Übersicht leidet bei beiden Techniken, ob du nun einen schwer leserlichen Formatstring oder eine tief verschachtelte Kette hast.
Nur das man den Formatstring vernünftig (an einer Stelle) dokumentieren kann, ein If-Else Monster unter der Dokumentation warscheinlich nur noch mehr leidet.

Dr.Doom
2008-06-12, 19:01:13
Nur das man den Formatstring vernünftig (an einer Stelle) dokumentieren kann, ein If-Else Monster unter der Dokumentation warscheinlich nur noch mehr leidet.Ja, aber die Anwendung im ersten Beitrag läuft ja quasi nur darauf hinaus, das Zeichen vor dem ersten Blank zu untersuchen, um zu erkennen, ob nur ein Zahl oder auch ein 'Zahl.Zahl' erlaubt ist.

In diesem Fall finde ich das pflegeleichter. *g*

del_4901
2008-06-12, 19:22:20
Ja, aber die Anwendung im ersten Beitrag läuft ja quasi nur darauf hinaus, das Zeichen vor dem ersten Blank zu untersuchen, um zu erkennen, ob nur ein Zahl oder auch ein 'Zahl.Zahl' erlaubt ist.

In diesem Fall finde ich das pflegeleichter. *g*

Die Zeichenkette soll beliebig sein, das ist mit Sicherheit nicht pflegeleichter, da ist nämlich noch ein for ringsrum, dann muss man noch einen Schritt zurück gehen, dabei darf man keinen negativen Index haben und und und...
Zumal wenn das Adressformat mal geändert werden sollte, man richtig angefickt ist.

Gast
2008-06-12, 19:29:01
.... DBB 4, DBX 2.1 , EW 2, E 0.0 ...


Ist zwar OT, aber wenn ich das sehe muss ich auf Siemens Simatic tippen ^^

Monger
2008-06-12, 20:35:52
Da stimme ich HoVa zu. Die Regex-Funktion ist auf unterster Ebene nichts anderes als Bedingungsmatrizen. Die Übersicht leidet bei beiden Techniken, ob du nun einen schwer leserlichen Formatstring oder eine tief verschachtelte Kette hast.
Unleserlich ist bei Regex sowieso alles. Ich hab irgendwo mal den sehr treffenden Satz aufgeschnappt: "Reguläre Ausdrücke gehören zu den Dingen die man nur schreibt, aber nicht liest."

Ich hab aber bisher überhaupt noch keine treffende Lösung gefunden, egal ob elegant oder unelegant. Naja, morgen setze ich mich nochmal dran.

Ist zwar OT, aber wenn ich das sehe muss ich auf Siemens Simatic tippen ^^
Rüschtüsch! :D
Das lässt mich vermuten, dass außer Siemens wirklich niemand dieses verquere Addressierungsschema verwendet.

HoVa
2008-06-12, 20:56:43
Nur das man den Formatstring vernünftig (an einer Stelle) dokumentieren kann, ein If-Else Monster unter der Dokumentation warscheinlich nur noch mehr leidet.

aber es muss doch nicht zwangslaeufig ein "if-else-Monster" enstehen?! Die (in Bezug auf den Regex) vereinfachte Variante laesst doch nur 2 Regex enstehen, und nicht mehr?

del_4901
2008-06-12, 20:58:49
Unleserlich ist bei Regex sowieso alles. Ich hab irgendwo mal den sehr treffenden Satz aufgeschnappt: "Reguläre Ausdrücke gehören zu den Dingen die man nur schreibt, aber nicht liest."

Ich hab aber bisher überhaupt noch keine treffende Lösung gefunden, egal ob elegant oder unelegant. Naja, morgen setze ich mich nochmal dran.


Man kann doch mit ^ Zeichen ausschließen, dann wird es doch nicht so schwer:

{Adresse} := {Buchstabe}* . ({String mit BWD am Anfang} + {String ohne BWD am Anfang})

{String mit BWD am Anfang} := ({Buchstabe mit BWD} . {Ziffer folgt auf Freizeichen} )

{String ohne BWD am Anfang} := ({Buchstabe ohne BWD} . {Ziffer folgt auf Freizeichen} . {Ziffer folgt auf Punkt})

{Buchstabe mit BWD} := ['B' + 'W' + 'D']

{Buchstabe ohne BWD} := !['B' + 'W' + 'D']

{Ziffer folgt auf Freizeichen} := [' '] . {Ziffer}

{Ziffer folgt auf Punkt} := ['.'] . {Ziffer bis 7}

* -> beliebige Anzahl
. -> Konkatination
+ -> Oder (Auswahl)
! -> Ausschluss (Negation)


Oder liege ich da jetzt falsch? Ich habs abgesehen von theoretischen Überlegungen auf Papier nie gebraucht. Ist manchmal ganz praktisch, wenn man sich einen Automaten bauen will.
aber es muss doch nicht zwangslaeufig ein "if-else-Monster" enstehen?! Die (in Bezug auf den Regex) vereinfachte Variante laesst doch nur 2 Regex enstehen, und nicht mehr?
Ich kann das natürlich so refactorn, das auch ein if/else Monster übersichtlich bleibt, aber wiso das Rad jedesmal neu erfinden müssen, wenn es schon String-Matching-Techniken gibt?

HoVa
2008-06-12, 21:06:23
Na ich sehe das so:
Regex sind schon problematisch genug. Daher sollte man sie moeglichst einfach halten. Und in diesem Fall halt ich es eben fuer sinnvoll, das Problem aufzusplitten. Ich erhalte zwei etwas einfachere Regex. Gut ich muss dann auch zwei Regex warten und pflegen. Auf der anderen Seite, (wie gehabt) wenn man schon nicht fit in dem Thema ist, sollte man sich diese Seite wenigstens leicht gestalten.

Ist vielleicht letztendlich auch nur Geschmackssache.

EDIT:
Das hier ist ein Ansatz fuer den 2ten RegEx:
\p{L}{0,10}(?<!B|D|W)\s\d\.?\d?
Wenn kein B,D,W dann ist danach genau ein Punkt und ein Digit erlaubt. Weiss aber nicht, ob das genau dem Use-Case entspricht, weil ganz am Schluss nur eben genau ein Digit angezogen wuerde (--> wegen dem letzten Fragenzeichen: 0 bis 1).

del_4901
2008-06-12, 21:11:42
Na ich sehe das so:
Regex sind schon problematisch genug. Daher sollte man sie moeglichst einfach halten. Und in diesem Fall halt ich es eben fuer sinnvoll, das Problem aufzusplitten. Ich erhalte zwei etwas einfachere Regex. Gut ich muss dann auch zwei Regex warten und pflegen. Auf der anderen Seite, (wie gehabt) wenn man schon nicht fit in dem Thema ist, sollte man sich diese Seite wenigstens leicht gestalten.

Ist vielleicht letztendlich auch nur Geschmackssache.
Kann man die RegEX nicht auch irgendwie verodern? Da gibs doch bestimmt fertige Klassenfunktionen. Kann mich auch irren, hab das noch nie verwenden müssen.

HoVa
2008-06-12, 23:04:46
Ja das geht, aber nur in wenigen Sprachen:

"Relativ wenig verbreitet sind bedingte Ausdrücke. Diese sind u. a. in Perl, PCRE und dem .NET Framework einsetzbar. Python bietet für solche Ausdrücke in Zusammenhang mit look-around assertions nur eingeschränkte Funktionalität."
Quelle: Wikipedia.org

Wuerde dann in etwa so aussehen:
\p{L}{0,10}(?(?=(?<!B|D|W))\s\d\.?\d?|\s\d(?!\.\d))
(Ist ungetestet, da ich grad nur Java benutzt hab und Java es leider nicht unterstuetzt.)

Und nochmal zum Vergleich:
\p{L}{0,10}(?<=B|D|W)\s\d(?!\.\d)
+
\p{L}{0,10}(?<!B|D|W)\s\d\.?\d?

Bei den letzten beiden kann ich drueber schauen und verstehe sie sofort (vor allem wenn sie schoen untereinander stehen ;)), beim ersten brauch man (imho) einen Moment laenger.

Hat jemand vielleicht noch ne Idee, wie man es doch in Java etc. realisieren koennte?

del_4901
2008-06-12, 23:19:33
Ja das geht, aber nur in wenigen Sprachen:

"Relativ wenig verbreitet sind bedingte Ausdrücke. Diese sind u. a. in Perl, PCRE und dem .NET Framework einsetzbar. Python bietet für solche Ausdrücke in Zusammenhang mit look-around assertions nur eingeschränkte Funktionalität."
Quelle: Wikipedia.org

Wuerde dann in etwa so aussehen:
\p{L}{0,10}(?(?=(?<!B|D|W))\s\d\.?\d?|\s\d(?!\.\d))
(Ist ungetestet, da ich grad nur Java benutzt hab und Java es leider nicht unterstuetzt.)

Und nochmal zum Vergleich:
\p{L}{0,10}(?<=B|D|W)\s\d(?!\.\d)
+
\p{L}{0,10}(?<!B|D|W)\s\d\.?\d?

Bei den letzten beiden kann ich drueber schauen und verstehe sie sofort (vor allem wenn sie schoen untereinander stehen ;)), beim ersten brauch man (imho) einen Moment laenger.

Hat jemand vielleicht noch ne Idee, wie man es doch in Java etc. realisieren koennte?
Der erste ist AFAIK auch nur zusammengefasst und optimiert, was hindert dich dran letzteres nicht zu tun? Monger wollte eine Lösung haben, nicht eine optimale.

HoVa
2008-06-13, 00:55:00
hier mal noch eine wirklich veroderte Loesung:
\p{L}{0,10}((?<!B|D|W)\s\d\.?\d?|(?<=B|D|W)\s\d(?!\.\d?))

Und ich empfehle doch lieber eine optimale Loesung, als eine Loesung?

Monger
2008-06-13, 10:16:45
Also, die veroderte Lösung funktioniert schonmal. Danke!

Ich hab nochmal ein bißchen rumgespielt, und bin auf folgende Lösung gekommen:

(?>\A\w*?([BWD])?\s?\d+)(?(1)|\.[0-7])\Z

Zuerst einmal ist also \w* lazy, damit ein eventuelles BWD auch wirklich in der Gruppe ankommt. \Z verhindert, dass bei einem MB 0.0 die Suche einfach schon bei MB 0 erfolgreich abschließen kann, \A verhindert das selbe vorne. (?>) wiederum verhindert, dass der ganze Ausdruck bis ganz vorne zurückgerollt werden kann, um ein potentielles BWD eben doch noch dem \w* zuzuordnen (darüber bin ich vorher nämlich ständig gefallen).

Was jetzt schöner ist, darüber kann man natürlich streiten. Mir ging es jetzt vorallem darum, das If/then/else zu begreifen, weil das scheint mir doch ein ziemlich mächtiges Konstrukt zu sein - und .NET unterstützt es ja glücklicherweise.

HoVa
2008-06-13, 11:10:45
Ich hab maechtig Probleme deinen RegEx zu verstehen.
Es ist wirklich interessant zu sehen, wie unterschiedlich man dieses Problem angehen kann.

Gefaellt mir! :D

EDIT:
Was willst du denn damit genau ausdruecken?
"(?(1)"

und \A soll fuer ^ stehen (Zeilenanfang) und \Z fuer $ (Zeilenende), ne?

PHuV
2008-06-13, 11:15:41
Was mich bei den ganze RegExs so stört ist die Tatsache, daß bei der Implementierung jeder immer ein bißchen was anderes daraus mach, sei es Perl, Java (Ja, das kann es auch) Shells usw. Man muß immer rumexperimentieren, und man hat keine Gewährleistung, daß es mit einem anderen Tool dann genau so funktioniert. :mad: