Laden...

Verständnisproblem mit Regex

Erstellt von Jacyrio vor 8 Jahren Letzter Beitrag vor 8 Jahren 1.086 Views
J
Jacyrio Themenstarter:in
197 Beiträge seit 2006
vor 8 Jahren
Verständnisproblem mit Regex

Guten Tag,

heute habe ich ein Problem mit einem Regex-Ausdruck. Ich glaube ja, das Regex ziemlich cool ist, wenn man denn mal in der Syntax durchgeblickt hat. Da ich dort noch leichte Verständnisprobleme habe (auch nach mehrmaligen durchlesen von Tutorials..) hoffe ich, ihr könnt mir erklären, wo mein Problem ist.

Also folgendes:
Ich habe einen String der so aussieht

<%VariableA(018)%>{:13:}<%VariableB-X(012)%>

Nun habe ich folgenden Regex-Ausdruck angewandt:


Regex rx= new Regex("<%.*%>");

Und möchte per foreach die einzelnen matches durchlaufen:


foreach (Match match in rx.Matches(s))
{
....
}

Ich möchte im ersten Schleifendurchlauf <%VariableA(018)%> und im zweiten <%VariableB-X(012)%>. Mein Problem ist, dass er nicht beim ersten %> aufhört, sondern erst beim letzten. Ich bekomme also den kompletten String zurück.

Ich habe hier schon was von "gierigen" und "genügsames" Verhalten gelesen. Das wird hier wahrscheinlich auch mein Problem sein. Ändere ich den Regex-Ausdruck auf


Regex rx= new Regex("<%[a-zA-Z0-9()]*%>");

ab, erhalte ich auch mein gewünschtes Ergebnis. Allerdings kann zwischen <% %> jedex X-Beliebige (also ".") stehen und nicht nur die Zeichen oben in den eckigen Klammern.

Was mache ich hier falsch?

Außerdem ein Schritt weiter:
Sobald ich den Ausdruck <%VariableA(018)%> habe, habe ich geplant einen weiteren regex-Ausdruck auf diesen Teilstring anzuwenden, um z. B. die (018) zu finden. Ist das Vorgehen so OK oder gibt es da eine bessere Alternative für?

Vielen Dank schon mal wieder...

16.834 Beiträge seit 2008
vor 8 Jahren

Schau Dir [Artikel] Regex-Tutorial an, damit Du mit Regex warm wirst.
zB haben () eine besondere Bedeutung, die Du entsprechend escapen musst, wenn Du sie als Zeichen behandeln willst - ebenso % und alle anderen Keys.

J
Jacyrio Themenstarter:in
197 Beiträge seit 2006
vor 8 Jahren

Hey Abt,

genau das Tutorial habe ich mir durchgelesen. Meinen Fehler finde ich aber trotzdem nicht. Das mit dem Escapen habe ich versucht (auch wenn ich nicht wusste, dass %-Zeichen auch eine bestimmte Bedeutung haben).. aber auch das führt nicht zu meinem Ergebnis 😦

2.079 Beiträge seit 2012
vor 8 Jahren

Dass das %-Zeichen eine gesonderte Bedeutung hat, ist mir allerdings auch neu 😄

Die einfachste Variante wird wohl sein, dass Du in deinem ersten Pattern hinter dem *-Zeichen ein Fragezeichen setzt. Das unterdrückt dieses gierige Verhalten, ist allerdings langsamer.

Eine schneller Variante ist, wenn Du für den zu findenden Text nicht eine feste Menge Zeichen vorgibst, die verwendet werden darf, sondern eine Menge Zeichen, die nicht verwendet werden darf. Wie das geht, findest Du im verlinkten Tutorial unter dem Punkt 4.4
Dann sagst Du einfach, es ist alles erlaubt, außer das %-Zeichen.

Außerdem brauchst Du eine Gruppe um an die gesuchten Texte zu kommen. Die würde ich aber immer benennen, denn dann hast Du deinen Namen, der im Code leichter zu lesen ist als der Index.
Auf die Treffer kannst Du dann zugreifen, indem Du nach der Gruppe mit dem Namen suchst und dir davon die Ergebnisse geben lässt:

var myGroupCaptures = match.Groups["MyGroup"].Captures;

Allerdings befindet sich das alles auch in dem Tutorial 😉
Nagut, nicht alles, der letzte Code-Schnipsel ist da glaube nicht so einfach zu finden, da muss man schon googlen.

Ansonsten ist noch diese ganz gut.
Da lässt sich die Arbeit auch speichern (STRG + S) und mit einem eindeutigen Link weiter geben.
Ist offiziell für den PCRE-Standart ausgelegt, funktioniert aber in (fast) allen Punkten auch für die Implementierung in C#

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

16.834 Beiträge seit 2008
vor 8 Jahren

Um folgende Zeichen zu verwenden, muss man diese auch escapen: \ ( ) [ { | * ? + ^ $ . # [Leerzeichen]

Wobei es bei POXIS eben BRE und ERE Variation gibt.

Regex rx= new Regex("<%[a-zA-Z0-9()]*%>");  

Sieht aber nicht so aus 😉

Ich teste Regex gerne mit https://regex101.com/
Bestes Tool, das ich dafür kenne. Das hier zu Lösen hat nun 3 Minuten gebraucht.

Und für mich funktioniert folgendes (wahrscheinlich kanns nen Profi einfacher): <%\w+\((?<First>\d+)\)]*%>(?<Data>.+?)<%[\w+-]{1,}\((?<Second>\d+)\)%>
https://regex101.com/r/uI0tN0/3

.. ich war der Meinug auch % hatte eine Sonderrolle =)

2.079 Beiträge seit 2012
vor 8 Jahren

.. ich war der Meinug auch % hatte eine Sonderrolle =)

Probier mal aus, geht auch ohne Escapen 😉
Ich sehe gerade - hast Du ja schon gemacht 😄

Prinzipiell würde ich aber sagen: Im Zweifel einfach escapen, das funktioniert immer 😄

An sich hast Du aber schon den einfachsten Weg gewählt.
Einzigen Nachteil sehe ich darin, dass es für die Werte recht eingeschränkt ist. Daher würde ich einfach alles bis auf das %- bzw. das <-Zeichen verbieten.

Meine Variante:

<%(?<variable>[^%]*\((?<value>[^\)]*)\))%>(?<content>[^<]*)<%(?<variable>[^%]*\((?<value>[^\)]*)\))%>

https://regex101.com/r/uI0tN0/4

Gibt mir das zurück:

variable	[2-16]		`VariableA(018)`
value		[12-15]		`018`
content		[18-24]		`{:13:}`
variable	[26-41]		`VariableBX(012)`
value		[37-40]		`012`

Außerdem müssen Gruppen, die sich wiederholen, keinen eindeutigen Namen haben. Im Standart muss mit dem Pattern dann der J-Parameter mit gegeben werden, damit das erlaubt wird.
In C# ist das aber immer erlaubt, die verschiedenen Treffer können dann über die Group.Captures-Property abgerufen werden.

@Jacyrio:

Wenn der eigentliche Text aber nicht nur nach XML aussieht sondern auch ungefähr so komplex werden kann, dann würde ich aber immer mehrere Patterns verwenden, das macht die Sache deutlich leichter zu lesen und auch deutlich flexibler für Erweiterungen.

So würde ich ein Pattern für den Text in den spitzen Klammern machen, ein Pattern für den Text zwischen den XML-ähnlichen Tags, ein Pattern für den Wert in den Klammern hinter den Variablen - und so weiter.
Mit einer Baumstruktur kann dann das Format beschrieben und geparst werden.

Beachte aber immer: Regex ist sch**** langsam 😄
Wenn es Performance eine große Rolle spielt, dann ist es besser, den String händisch zu parsen.
Dann sollte die Performance aber auch wirklich eine große Rolle spielen, im Zweifel lieber auf Code-Qualität achten, optimieren kann man hinterher immer noch.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

J
Jacyrio Themenstarter:in
197 Beiträge seit 2006
vor 8 Jahren

Hey,

danke für die Tipps.. muss ich jetzt erst mal durchblicken 😄 Aufjedenfall ist das nur ein Ausschnitt des Strings. Der String kann noch viel länger werden (bzw. ist länger) und hat noch viel mehr von diesen "Tags". Am Ende benutze ich den String, um daraus einen fertigen Report für einen Nadeldrucker zu erstellen.

Die Tags werden durch Properties eines Objekts ersetzt.. die Zahl in der Klammer ist die maximale Anzahl von Zeichen. Ein Tag könnte aber zB auch so aussehen <%{VariableA}(100)(04)(#)(R)%> soll heißen: Variable A soll ersetzt werden, darf maximal 100 Zeichen lang sein, maximal 4 Zeilen haben, die restlichen Zeichen werden nach Rechts (R) mit # aufgefüllt. {:13:} bedeutet zB 13 Leerzeichen und [:5:] würde bedeuten 5 ReturnLines.

Ich vermute mal, nun wird jemand kommen und mir sagen wie ich es besser machen kann.. aber mir ist auf Anhieb nichts anderes eingefallen. Ich hatte erst mal die Idee das vielleicht mit XML zu bauen, wusste aber nicht wie genau und habe mich dann dazu entschieden den String selbst zu erstellen und mit Regex auseinander zu nehmen und zu parsen..

P.S. den String bekomme ich übrings aus einer Textfile. Habe ich deshalb gemacht, damit auch andere Benutzer das Design anpassen könnten.. vorausgesetzt sie verstehen, was dadrin steht =D

49.485 Beiträge seit 2005
vor 8 Jahren

Hallo zusammen,

der Tipp mit dem Fragezeichen, um das gierige Verhalten abzuschalten, kann bei bestimmten Pattern auch nach hinten losgehen. Statt .*? ist [^%>]* besser, sofern die Zeichen auch einzeln nicht erlaubt sind. Sie sie nur zusammen als %> nicht erlaubt, dann ((?!%>).)*

In RegEx kürzester Match [und die Gefahren von .*?] habe ich das Prinzip genauer beschrieben.

herbivore