Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   RegEx-Formatierung (https://www.delphipraxis.net/186176-regex-formatierung.html)

hansklok 10. Aug 2015 22:12

RegEx-Formatierung
 
Hallo ;)

Ich würde gern folgende Datumsangaben via RegEx splitten:
Delphi-Quellcode:
AUG 1917
2 AUG 1917
02 AUG 1917
1917B.C.
1917BC // wird nicht korrekt geparst
1917 B.C.
1917/18
1917/18 B.C.
ABT AUG 1917
INT 2 AUG 1917 (Mamas Geburtstag) // hier soll nur alles innerhalb der Klammer geparst werden, wobei Klammern und Inhalt ebenfalls optional sind
@#DGREGORIAN@ CAL 17 AUG 1917 // wird nicht korrekt geparst
@#DJULIAN@ 02 AUG 1917 // wird nicht korrekt geparst
@#DFRENCH R@ 02 VEND 1917 // wird nicht korrekt geparst
@#DHEBREW@ 02 TIS 5250 // wird nicht korrekt geparst
Bisher mache ich das so:
^(@#D(GREGORIAN|JULIAN|HEBREW|FRENCH\s*R)@)?\s*(AB T|EST|CAL|INT)?\s*(\d*)\s*([a-zA-Z]*)?\s*(\d*)?(\s*/)?(\d*)?\s*(BC|B.C.)?\s*(.*)?$

Das Parsen des Kalender Kalender funktioniert allerdings nicht richtig. Ich möchte nur, dass z.B. nur @#DHEBREW@ anstatt @#DHEBREW@ und HEBREW gemacht wird. Was muss ich abändern?

LG

idefix2 10. Aug 2015 23:53

AW: RegEx-Formatierung
 
Was meinst du mit "nur ... gemacht wird"?

hansklok 11. Aug 2015 00:17

AW: RegEx-Formatierung
 
Zitat:

Zitat von idefix2 (Beitrag 1311732)
Was meinst du mit "nur ... gemacht wird"?

Naja, jetzt gibt es sowohl ein Match, welches @#DHEBREW@ ausgibt und als zweiten Match eben DHEBREW. Ich möchte aber nur DHEBREW ausgegeben bekommen.

idefix2 11. Aug 2015 09:06

AW: RegEx-Formatierung
 
Normalerweise ist alles, was zwischen ( und ) steht, ein gefundener Unterausdruck, der in match[i] gemerkt wird. So viele Klammerpaare, soviele mögliche Unterausdrücke.
Lösung 1: Du merkst dir, welche Unterausdrucke dich interessieren (die Numerierung ist statisch, egal ob die Ausdrücke gefunden werden oder nicht). Alternativen müssen natürlich zwischen Klammern stehen, egal, ob du den entsprechenden Unterausdruck brauchst oder nicht.
Lösung 2: hängt von der Regex-Implementierung ab. Die meisten Implementierungen, die ich kenne, stellen eine Möglichkeit zur Verfügung, einen geklammerten Teil zu kennzeichnen, sodass der darin gefundene Inhalt nicht als Teilmatch registriert wird. Das müsstest du aber der Dokumentation deiner Regex-Implementierung entnehmen, weil das ist leider in der Sprache nicht genormt. Welche Regex-Routinen verwendest du?

hansklok 11. Aug 2015 11:09

AW: RegEx-Formatierung
 
@idefix2: ich nutze die "PCRE library". Das ist eine Perl kompatible Bibliothek.
Du sprichst von Untergruppen (Subgroups, oder). Davon habe ich gehört, verstehe aber deren Verwendung und Implementierung nicht.

idefix2 11. Aug 2015 11:53

AW: RegEx-Formatierung
 
In der Hilfe zu PCRE (Syntax specification) steht:

(?:...) non-capturing group

Code:
^(@#D(?:GREGORIAN|JULIAN|HEBREW|FRENCH\s*R)@)?\s*(AB T|EST|CAL|INT)?\s*(\d*)\s*([a-zA-Z]*)?\s*(\d*)?(\s*/)?(\d*)?\s*(BC|B.C.)?\s*(.*)?$
sollte also funktionieren

idefix2 11. Aug 2015 12:05

AW: RegEx-Formatierung
 
Was bei
1917BC // wird nicht korrekt geparst
nicht funktionieren soll, ist mir nicht klar.
Allerdings sind die Punkte beim B.C. problematisch, die musst du im Regex escapen, weil ein Punkt erkennt jedes beliebige Zeichen als gültig und es würde auch z.B. BIC7 als gültig erkannt werden.

also (BC|B\.C\.)

Zitat:

INT 2 AUG 1917 (Mamas Geburtstag) // hier soll nur alles innerhalb der Klammer geparst werden, wobei Klammern und Inhalt ebenfalls optional sind
Also was jetzt, willst du dass (ABT|EST|CAL|INT) gefunden wird oder nicht?

Nebenbei: Was soll
@#DFRENCH R@ 02 VEND 1917
sein? Vendredi ist doch ein Wochentag und kein Monatsname?

Noch was:
(\d*)? und (.*)?
Da ist das Fragezeichen unnötig, * bedeutet ja 0 mal oder öfter

hansklok 11. Aug 2015 20:30

AW: RegEx-Formatierung
 
Zitat:

Zitat von idefix2 (Beitrag 1311785)
Code:
^(@#D(?:GREGORIAN|JULIAN|HEBREW|FRENCH\s*R)@)?\s*(AB T|EST|CAL|INT)?\s*(\d*)\s*([a-zA-Z]*)?\s*(\d*)?(\s*/)?(\d*)?\s*(BC|B.C.)?\s*(.*)?$
sollte also funktionieren

Das funktioniniert schon mal hervorragend. Dankeschön dafür.
Zitat:

Zitat von idefix2 (Beitrag 1311789)
Was bei
Code:
1917BC // wird nicht korrekt geparst
nicht funktionieren soll, ist mir nicht klar. Allerdings sind die Punkte beim B.C. problematisch, die musst du im Regex escapen, weil ein Punkt erkennt jedes beliebige Zeichen als gültig und es würde auch z.B. BIC7 als gültig erkannt werden.
also (BC|B\.C\.)

So, das wird mittlerweile mittels folgendem String erkannt:
Code:
^(@#D(?:GREGORIAN|JULIAN|HEBREW|FRENCH\s*R)@)?(?:\s*)(ABT|EST|CAL|INT)?(?:\s*)(\d*)?(?:\s*)(JUN|AUG|VEND|TIS)?(?:\s*)(\d*)?(?:/)?(\d*)?(B\.C\.|BC)?$
Allerdings nach wie vor nicht die folgende Zeile:
Code:
1917/18 B.C.
Zitat:

INT 2 AUG 1917 (Mamas Geburtstag) // hier soll nur alles innerhalb der Klammer geparst werden, wobei Klammern und Inhalt ebenfalls optional sind.
Also was jetzt, willst du dass (ABT|EST|CAL|INT) gefunden wird oder nicht?
Also zur Erklärung des Datumseintrages der erkannt werden soll:

Kalendersystem (optional)
Wird keine dieser Angaben gefunden wird automatisch das Datum als Datum des gregorianischen Kalenders angenommen.
  • @#DGREGORIAN@
  • @#DJULIAN@
  • @#DHEBREW@
  • @#DFRENCH R@
Qualität (optional)
  • CAL (berechnetes Datum)
  • EST (geschätztes Datum)
Datum
  • Tag (optional)
  • Monat (optional, Monatsnamen abhängig vom Kalendersystem)
  • Jahr
    • duales Jahr (optional)
  • B.C./BC (optional, nur möglich, wenn Jahresangabe alleine ohne Tag und Monat notiert ist)
Datum als Freitext
  • INT (Tag) (Monat) Jahr (Freitext)
  • (Freitext)
Das ist also der Aufbau der Datumsnotation. Vielleicht hilft das beim Nachvollziehen.
Zitat:

Nebenbei: Was soll
@#DFRENCH R@ 02 VEND 1917
sein? Vendredi ist doch ein Wochentag und kein Monatsname?
Richtig, es ist Vendmaire (Monat des republikanischen Kalenders) gemeint ;)
Zitat:

Noch was:
(\d*)? und (.*)?
Da ist das Fragezeichen unnötig, * bedeutet ja 0 mal oder öfter
Das hast Du natürlich völlig recht. Ich arbeite mich grade erst in die regulären Ausdrücke ein.

idefix2 11. Aug 2015 22:17

AW: RegEx-Formatierung
 
Ich frage mich, ob es wirklich gescheit ist, ein derart aufwändiges Parsing mit einem einzigen Regex zu erschlagen, oder ob es nicht gescheiter wäre, die doch zum Teil grundverschiedenen Formate separat zu handhaben.

Eine Gruppe (?:\s*) zu bilden bringt gegenüber \s* keinen Vorteil. Ich würde per (?x) die erweiterte Syntax einschalten, dann kannst du im Regex Leerstellen zur besseren Lesbarkeit verwenden, Leerstellen werden dann für das Parsen ignoriert.

(\d*)?(?:/)?(\d*)?
ist nicht gut. Ich gehe davon aus, dass der Schrägstrich nicht allein auftreten darf, sondern nur als Trennung zwischen zwei Zahlengruppen (Jahreszahlen).
Also: (\d+/\d+|\d*)
Mit der 2. Alternative deckst du auch den Fall ab, dass das Jahr ganz weggelassen wird, es ist also auch nach dieser Gruppe kein ? nötig.

hansklok 11. Aug 2015 22:24

AW: RegEx-Formatierung
 
Zitat:

Zitat von idefix2 (Beitrag 1311874)
Eine Gruppe (?:\s*) zu bilden bringt gegenüber \s* keinen Vorteil. Ich würde per (?x) die erweiterte Syntax einschalten, dann kannst du im Regex Leerstellen zur besseren Lesbarkeit verwenden, Leerstellen werden dann für das Parsen ignoriert.

OK, und wie genau gehe ich da jetzt vor? Step by Step? Leuchtet meine Erklärung zum Aufbau des Datums ein?

idefix2 11. Aug 2015 22:39

AW: RegEx-Formatierung
 
Code:
(?x) ^  (@#D(?:GREGORIAN|JULIAN|HEBREW|FRENCH\s*R)@  \s*)?    (?:(ABT|EST|CAL|INT) \s*)?  (?:(\d+) \s*)? 
(?:(JUN|AUG|VEND|TIS) \s*)? (\d+/\d+|\d*) \s* (B\.C\.|BC)?  $
Wenn nach dem BC keine Leerstellen folgen dürfen, sonst am Ende vor dem $ nochmal \s*

Die Erklärung zum Aufbau des Datums verstehe ich schon, nicht klar ist mir, was man mit so inhomogenen Datumsangaben in der Folge anfangen kann - aber das musst du wissen :)

hansklok 11. Aug 2015 22:43

AW: RegEx-Formatierung
 
Zitat:

Zitat von idefix2 (Beitrag 1311878)
Die Erklärung zum Aufbau des Datums verstehe ich schon, nicht klar ist mir, was man mit so inhomogenen Datumsangaben in der Folge anfangen kann - aber das musst du wissen :)

http://genwiki.genealogy.net/GEDCOM/DATE-Tag 8-)

idefix2 11. Aug 2015 22:54

AW: RegEx-Formatierung
 
ok.

Was noch fehlt, ist die Behandlung des Klartexts in Klammern am Schluss:

Code:
(?:\( ([^)]*) \) \s* )
liefert dir den Text zwischen den Klammern, ohne die Klammern selbst - vorausgesetzt, der Text in den Klammern enthält selbst keine schliessende Klammer. Wenn das auch erlaubt sein soll, wäre es einfach (.*) statt ([^)]*)

hansklok 11. Aug 2015 23:13

AW: RegEx-Formatierung
 
Schau mal bitte hier mein Beispielcode http://rubular.com/r/8DBJT9K0tH. Das matcht leider noch nicht alles. :(
Zumal die dualen Jahresangaben nur gesamt und nicht gesondert gematcht werden. Ich benötige aber jeweils einen Match für 1917 und 18.

idefix2 11. Aug 2015 23:40

AW: RegEx-Formatierung
 
Klar.
Das ? nach dem letzten Klammerausdruck fehlt, deswegen matcht er nur die drei Ausdrücke, die (Muttis Geburtstag) enthalten.
Deshalb: (?:\( ([^)]*) \) \s* )?

Ich denke, es ist einfacher, die verbleibenden Probleme im Programmcode als via Regex zu lösen: Die Unterscheidung, welche Ziffern Jahreszahlen und welche Tage sind, ist im Regex schwierig - da müsste man Riesen Teilausdrücke duplizieren, um die verschiedenen Varianten richtig zu unterscheiden.
Die Jahreszahlen könntest du natürlich getrennt einfangen, dann bekommst du aber DREI verschiedene Ergebnisfelder für "einzige Jahreszahl", "Jahreszahl 1 von 2" und "Jahreszahl 2 von 2" - und ein Teil der Jahreszahl landet u.U. beim Tag, wenn kein Tag angegeben ist. Regex ist gut, aber derart komplexe Fallunterscheidungen sind auf der Programmebene besser aufgehoben.

Dejan Vu 12. Aug 2015 08:30

AW: RegEx-Formatierung
 
Ist das eine Denksportaufgabe oder warum machst Du das mit RegEx?

Ich würde das nämlich als kleine Heuristik programmieren, also à la 'If Pos('HEBREW, text) <> 0 then'.

Dann kannst Du unit tests schreiben und Probleme in Ruhe dursteppen. Versteh mich nicht falsch: regEx kann das, nur wird das eben irgendwann einmal so kompliziert, dass das micht mehr wartbar ist. Lass doch mal etwas dazukommen. Dann sitzt Du doch tagelang an der Anpassung...

idefix2 12. Aug 2015 09:20

AW: RegEx-Formatierung
 
Zitat:

Zitat von Dejan Vu (Beitrag 1311906)
Ist das eine Denksportaufgabe oder warum machst Du das mit RegEx?

Ich würde das nämlich als kleine Heuristik programmieren, also à la 'If Pos('HEBREW, text) <> 0 then'.

Regex ist Pos&Co in jedem Fall vorzuziehen, es ist ungleich bequemer. Schau dir an, wieviel Code du brauchst, um nur
Code:
(@#D(?:GREGORIAN|JULIAN|HEBREW|FRENCH\s*R)@)?\s*)
via Programm-Code zu scannen. Als regex ist das ein übersichtlicher, problemslos lesbarer Einzeiler (wobei ich mir ja eher den inneren und nicht den äusseren Klammerausdruck für die Weiterverarbeitung merken würde, wie er es macht). Ein Argument für die Verwendung von pos wäre da nur, dass du pro Programmzeile bezahlt wirst.

Allerdings würde ich derart komplexe Regexe eher aufspalten und dann per Programmlogik verknüpfen, vor allem dort, wo Abhängigkeiten zwichen den verschiedenen Varianten bestehen. Wenn du über einen einzigen Regex Tag, Monat und Jahr extrahieren willst, aber alle drei Angaben optional sind, wird es eben recht haarig. Das NUR mit Regex zu machen halte ich nicht für das Gelbe vom Ei, auch wenn es mit grossem Aufwand sicher möglich ist.

hansklok 12. Aug 2015 15:00

AW: RegEx-Formatierung
 
Zitat:

Zitat von Dejan Vu (Beitrag 1311906)
Ist das eine Denksportaufgabe oder warum machst Du das mit RegEx?

Eine berechtigte Frage. Ich habe das mal mit den bekannten Springfunktionen gemacht, aber da in einer GEDCOM-Datei dutzende Datumsangaben vorkommen und so eine Datei mal schnell 10000-1000000 Zeilen haben kann, dachte ich, ich nehme RegEx, da das parsen da deutlich schneller vonstatten geht.
Zitat:

Zitat von idefix2 (Beitrag 1311917)
Regex ist Pos&Co in jedem Fall vorzuziehen, es ist ungleich bequemer. Schau dir an, wieviel Code du brauchst, um nur
Code:
(@#D(?:GREGORIAN|JULIAN|HEBREW|FRENCH\s*R)@)?\s*)
via Programm-Code zu scannen.

Du sagst es.

Nun bisher wird ja auch nur eine Datumsangabe gepasst. Bedacht wurde noch nicht, dass es in Gedcom-Datumsangaben auch Doppeldatierung gibt:
Code:
BET 17 AUG 1917 AND CAL 21 AUG 1918

idefix2 12. Aug 2015 18:21

AW: RegEx-Formatierung
 
Ein Fallstrick sind die "optionalen" Werte und die Abhängigkeiten.
Wenn ich es richtig verstehe, sind je nach Datumstyp unterschiedliche Monatsnamen möglich. Dann ist noch die Frage, ob du überprüfen musst, dass das zusammenpasst, oder ob du dich darauf verlassen kannst. Im zweiten Fall ist es einfacher, denn da kannst du per Regex auch Kombinationen erlauben, die keinen Sinn machen, und die man deswegen als ungültig verwerfen müsste.

Ein weiteres Problem sind die
Code:
\s*
, denn irgend eine Trennung zwischen den Zahlen brauchst du: Also zumindest entweder Monatsnamen (Buchstaben) oder Leerstellen, oder beides, um Zifferngruppen zu trennen. Das muss auch in die Regex Logik hinein, sonst landet, wie in ein paar der Beispiele, ein Teil der Jahreszahl in dem Feld, in dem du den Tag erwartest. Im nachhinein per Programm, wie ich in einem Post weiter oben geschrieben habe, ist es wahrscheinlich gar nicht mehr möglich, das sauber auseinanderzudividieren.


Vielleicht ist es zielführend, erst einfache regexausdrücke zu definieren und den kompletten regex aus diesen einfacheren ausdrücken zusammenzusetzen:

const
einfachesdatum = '.....'; // was auch immer
datumsbereich = '(?:BET\s+' + einfachesdatum + '\s+AND\s+' + einfachesdatum + ')';
datum = '(?:' + datumsbereich + '|' + einfachesdatum + ')';

etc.

Eine andere Idee, um das Parsen zu vereinfachen, wäre, erst einmal den String durchzugehen und prinzipiell überall, wo Buchstaben und Zahlen unmittelbar aufeinanderfolgen, eine Leerstelle einzufügen. Dann kannst du im Regex Trennzeichen zwischen den Gruppen zwingend voraussetzen, mit \s+ statt \s* wird das Erkennen der Pattern stabiler und wahrscheinlich auch schneller.

Dejan Vu 12. Aug 2015 20:40

AW: RegEx-Formatierung
 
Zitat:

Zitat von idefix2 (Beitrag 1311917)
Regex ist Pos&Co in jedem Fall vorzuziehen, es ist ungleich bequemer.

Findest Du wirklich dieses RegEx-Monstrum 'bequemer'? Würdest Du bei einer Aufgabe dieser Art *wirklich* das Forum bemühen müssen, wenn Du die 'Code'-Methode verwenden würdest?
Zitat:

Schau dir an, wieviel Code du brauchst...via Programm-Code zu scannen.
Wenig Code != Viel Qualität.
Zitat:

Ein Argument für die Verwendung von pos wäre da nur, dass du pro Programmzeile bezahlt wirst.
Wir werden dafür bezahlt, effizient und qualitativ hochwertig zu programmieren. Wir wählen also immer die Methode, die für die Aufgabenstellung am besten geeignet ist. So sehr ich RegEx mag und auch oft einsetze, würde ich davon abraten, wenn ich mehrere Tage benötigte, um den Ausdruck zu formulieren. Das passt irgendwie nicht.
Zitat:

Allerdings würde ich derart komplexe Regexe eher aufspalten und dann per Programmlogik verknüpfen,
Das ist doch mal ein Vorschlag. :-)

Nicht falsch verstehen: RegEx an sich ist schon das richtige Mittel. Nur wenn es für den Programmierer zu komplex wird, wäre eine alternative Vorgehensweise durchaus in Erwägung zu ziehen.

Wenn Du das so hinbekommst, und auch bei Änderungen und Erweiterungen des Ausdrucks keine Probleme bekommst, super. Ich würde hier vermutlich doch etwas grübeln. Zeit ist Geld und daher nehme ich die Lösung, die langfristig am besten ist. Wobei ich mich neulich auch ertappt habe, ein RegEx zu finden, der bei einem SQL-Skript alle nicht deklarierten Variablen findet. Das war auch nicht ohne.... :mrgreen:

idefix2 13. Aug 2015 09:50

AW: RegEx-Formatierung
 
Zitat:

Findest Du wirklich dieses RegEx-Monstrum 'bequemer'?
Mein Vorschlag vom vorigen Post: Zerlegen in einfachere Teilausdrücke, die man dann als Alternativen und Verkettungen zu einem Regex zusammensetzt. Dann wird das ganze übersichtlich und wartbar.
Aufpassen muss man dann noch darauf, dass es "eindeutig" wird, d.h. dass der Regex-Parser die Teilausdrücke richtig zuordnet, das kann zum Fallstrick werden.

Zitat:

Würdest Du bei einer Aufgabe dieser Art *wirklich* das Forum bemühen müssen, wenn Du die 'Code'-Methode verwenden würdest?
Es ist nicht anders, als wenn ein Delphi-Anfänger hier im Forum Fragen zu irgend einem für einen geübten Programmierer trivialen Problem postet. Regex muss man lernen, wie alles andere auch, und solange man es nicht gelernt hat, ist es schwierig.

Zitat:

Wenig Code != Viel Qualität.
Wenn ich eine Aufgabe mit einem Einzeiler lösen kann, für die ich sonst Dutzende Codezeilen brauchen würde, dann ist das schon ein Kriterium.

Zitat:

So sehr ich RegEx mag und auch oft einsetze, würde ich davon abraten, wenn ich mehrere Tage benötigte, um den Ausdruck zu formulieren.
Wie lange du brauchst, um den Ausdruck zu formulieren, ist eine Frage deines Wissensstands zum Thema, abgesehen davon, dass du für das Ausprogrammieren auch eine Menge Zeit brauchst. Je komplexer der reguläre Ausdruck, umso komplexer ist das Programm, das du an Stelle dieses Ausdruck schreiben müsstest. Und ob es dann wirklich übersichtlicher würde, sei dahingestellt. Wenn man so etwas zum ersten mal verwendet, braucht man natürlich länger, bis man es hinbekommt - das ist aber nicht wirklich ein Argument.
Ich gebe aber zu, dass ich selbst einige "fortgeschrittene" Features der Regex-Syntax eher meide, weil das in meinen Augen dann wirklich unübersichtlich wird, und solche Parts eher an die Programmlogik delegiere. Nichtsdestotrotz ignorieren heute noch viel zu viele Programmierer die Möglichkeiten und Erleichterungen, die Regex bietet, und lösen viele Aufgaben extrem umständlich mit Hilfe von Pos und dergleichen.

Zitat:

Nicht falsch verstehen: RegEx an sich ist schon das richtige Mittel. Nur wenn es für den Programmierer zu komplex wird, wäre eine alternative Vorgehensweise durchaus in Erwägung zu ziehen.
Mein Reden :-D

Zitat:

Wobei ich mich neulich auch ertappt habe, ein RegEx zu finden, der bei einem SQL-Skript alle nicht deklarierten Variablen findet
Pffft :lol:


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:17 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz