RegEx: (.|\s)* für "alles"??

  • VB.NET

Es gibt 4 Antworten in diesem Thema. Der letzte Beitrag () ist von DeltaForce.

    RegEx: (.|\s)* für "alles"??

    Hey...

    Ich habe den Fehler gemacht, mit RegEx anzufangen. Da ich ungern sprichwörtliche lose Enden haben will, möchte ich jetzt aber nicht alles hinschmeißen und es anders lösen. Also...

    Relativ einfache Sache eigentlich. Ich versuche in einer syntaktisch C-ähnlich geschriebenen Skriptdatei was zu finden.

    Input:

    Quellcode

    1. VarScope("mapname")
    2. {
    3. VarScope("description")
    4. {
    5. VarBinary("GRM")
    6. {
    7. Size(90);
    8. Value("10001000140002007600270056005600E6000200D60016000700020016003700");
    9. Value("020022007600270056005600E600020037003600270056005600E60022000200");
    10. Value("6600F600270002003600160007004700570027009600E6007600");
    11. }
    12. }
    13. VarScope("name")
    14. {
    15. VarBinary("cor1")
    16. {
    17. Size(22);
    18. Value("200000003400F60027005700370036001600E6004700");
    19. }
    20. VarBinary("GRM")
    21. {
    22. Size(22);
    23. Value("100010007400270056005600E6000200D40016000700");
    24. }
    25. VarBinary("hot1")
    26. {
    27. Size(12);
    28. Value("300000008400F60047008600");
    29. }
    30. }
    31. }



    Pattern:

    Quellcode

    1. VarScope\("mapname"\)\s*\{(.|\s)*VarScope\("name"\)\s*\{(.|\s)*VarBinary\("GRM"\)\s*\{\s*Size\(\d+\);\s*Value\("[A-Za-z0-9]+"\);\s*\}


    Das ganze findet nur leider keinen Match... Zur Vereinfachung des Debuggings: Selbst folgendes Pattern klappt nicht.

    Quellcode

    1. VarScope\("mapname"\)\s*\{(.|\s)*VarScope\("name

    Es hapert also offenbar bei dem Bereich (.|\s)* welcher eigentlich "irgendwas zwischendrinnen, egal ob vorhanden oder wie viel" finden soll. Ist mein Denkansatz da falsch? Denn ein Punkt allein tut's ja nicht bei Zeilenumbrüchen.


    Jemand ne Idee, wo der Fehler liegt?

    Danke für jeden der crazy genug ist, sich in mein besch***- önte RegEx-Pattern reinzudenken :)
    Bei diesem Test funktioniert es einwandfrei (Auch, wenn ich die Multiline-Option rausnehme):


    Was möchtest Du denn finden?

    Übrigens: Wenn Du die Singleline-Option verwendest, findet der Punkt auch Zeilenumbrüche.

    Noch übrigenser: Zum Testen von RegEx-Patterns empfehle ich den RegEx-Tester von ErfinderDesRades: [VB 2010] Regextester - OpenSource
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Hi
    wenn's nicht jmd anderes ausm Forum löst, würde ich dir empfehlen, dir die String-Analyse selber anzutun. Die Struktur ist ja total einfach gehalten, daher ist der Einlesevorgang auch sehr einfach zu lösen. Schreib' dir eine Klasse, die deinen String mit einem Index kapselt und die einzelnen Tokens ausliest (Namen, wie VarBinary, Strings, Zahlen, Klammern, besondere Zeichen, wie ';' ). Die Tokens sollten dann eben von der Klasse in einem IEnumerable(Of Token) zur Verfügung gestellt werden. Token enthält dann den Tokentyp (Name, String, Zahl, etc.) und den Wert des Tokens und kann weiter prozessiert werden im nächsten Schritt.
    Im nächsten Schritt liest du die Tokens in eine Struktur ein. Das geht rekursiv sehr elegant, indem du eine Methode definierst, die einen Container lesen kann und eine, die eine Definition lesen kann. Definitionen beginnen mit einem Namen (somit wird als erstes auch die Interpretation von einer Definition angesteuert) und besitzen einen Container, der Nothing ist, sofern die Definition auf ein ';'-Token endet und einen Wert, der das angibt, was in den Klammern steht. Ungültige Eingaben führen zum Abbruch (z.B. FormatException oder Sammlung alle Syntax/Semantik-Fehler und anschließender Abbruch).
    Ich nenne die Klasse, die diese Informationen enthält jetzt auch mal Definition.
    Hier kommt's nun auf den Fall an. Ist der Name ausschlaggebend für die möglichen Definitionen innerhalb der Instanz? Wenn nicht, wäre die Methode unten sinnvoller.
    Ansonsten kann eine Definition dann anhand einer weiteren Klasse interpretiert werden. Die Klasse heißt jetzt einfach mal ContentElementProvider und Definition hat eine Funktion Function CreateContent(ByVal elementProvider As ContentElementProvider) As ContentElement wobei ContentElement eine abstrakte Klasse ist. ContentElementProvider hat eine Funktion Function CreateElement(ByVal name As String, ByVal arguments As IEnumerable(Of Object)) As ContentElement (oder, wenn nur 0-1 Argument verfügbar gemacht werden kann, einfach ein argument As Object, das in ersterem Fall auch Nothing sein kann). Anhand des Namens wird dann eine ContentElement-Instanz erzeugt, die ein IDictionary(Of String, ContentElement) bereitstellt und in die dann eben die Subelemente eingereiht werden. Das geschieht über den rekursiven Aufruf von Definition.CreateContent (jede Definition generiert anhand von sich selbst das Subelement, hier kann übrigens noch überprüft werden, ob die generierten Elemente überhaupt weitere Definitionen aufnehmen können, das wäre dann auf ContentElement zu definieren oder als Ausgabeparameter bei ContentElementProvider.CreateElement).

    Wenn die Elemente feste Eigenschaften besitzen, an die gebunden werden soll, bietest du pro Definition einfach eine Funktion Function CreateContent() As ContentElement an. Die Funktion erzeugt dann einfach das ContentElement rekursiv anhand der inneren Definitionen. D.h. erst werden die inneren Definitionen per CreateContent-Aufruf generiert und dann eben die äußere (ist halt statisch, nicht so schön, wie das mit den losgelösten Definitionen, aber es so zu beschreiben erhöht die Wahrscheinlichkeit, dass du es überhaupt in Erwägung ziehst).

    Btw. ich halt nicht viel von Regex bei solchen Definitionen. Regex ist einfach sehr statisch und mMn. unübersichtlich. Und btw. lehne meinen Beitrag nicht gleich von vorn herein ab, wie es die letzten male andere gemacht haben bei solchen Sachen. Es sieht vielleicht anfangs kompliziert aus, ist es aber nicht und wenn ich etwas nicht gesagt haben sollte/du etwas nicht verstanden haben solltest, sag' einfach Bescheid und ich erklär's noch mal. Überleg' auf jeden Fall, ob sich der Aufwand rentiert, das so zu lösen oder ob ein Regex-Pattern für dich praktischer ist. Regex führt später halt ggf. zu einem riesigen Wartungsaufwand, mein Konstrukt tendenziell eher nicht.

    Gruß
    ~blaze~
    Regex Pattern: VarScope\(\"name\"\)(.|\s)*?VarBinary\(\"GRM\"\)(.|\s)*?Size\((.*?)\)(.|\s)*?Value\(\"((.|\s)*?)\"\)
    Für VB: VarScope\(\""name\""\)(.|\s)*?VarBinary\(\""GRM\""\)(.|\s)*?Size\((.*?)\)(.|\s)*?Value\(\""((.|\s)*?)\""\)

    Gruppe 3 wäre die Size, Gruppe 5 dieses Value-Ding. Ich hoffe ich hab dein vorhaben richtig verstanden.


    Aber, ich empfehle dir auf garkeinen Fall (!!!) in diesem Falle RegEx zu benutzen, obwohl ich generell sehr begeistert davon bin. Ich wollte dir nur den Pattern geben weil du anscheinend viel Arbeit ins restliche zeugs gesteckt hast.

    Schau dir den Vorschlag von ~blaze~ an, mit dem kannst du viel mehr anfangen und es ist sicherlich viel einfacher und wartungsfreundlicher als diesen hässlichen Pattern zu verwenden.

    Niko Ortner schrieb:

    Übrigens: Wenn Du die Singleline-Option verwendest, findet der Punkt auch Zeilenumbrüche.
    Habe die Singleline-Option jetzt mit reingenommen und schon funktioniert es - wenn ich nen einfachen . anstatt dem (.|\s)* nehme. Das kommt mir zwar etwas seltsam vor, da (.|\s)* ohne Singleline doch einem . mit Singleline entsprechen müsste?

    @ ~blaze~:
    Ich konnte deinem Beitrag zwar nicht ganz folgen - was sicher zu einem gewissen Anteil auch daran liegt, dass ich mich mit diesem IEnumerable-Zeug noch nie auseinandergesetzt habe - , aber ich glaube ich konnte die Idee dahinter verstehen: Das gesamte Dokument mit seiner Struktur in Objekten zu kapseln, auf die man dann komfortabel und strukturiert zugreifen und so schnell verschiedene Werte an bestimmten Stellen in der Struktur auslesen kann.
    Das ist ehrlich gesagt für diese Situation etwas overcommittet, da ich eigentlich lediglich die Value- und evtl. auch Size-Werte bei "GRM" unter "name" und "description" auslesen möchte. Aber ich werde mir deinen Ansatz auf jeden Fall merken (und vielleicht irgendwann nochmal darauf zurückkommen), da das für eine andere Sache - nach dem wie ich es verstanden habe - genau das ist, was ich suche: Ein stark strukturiertes Dokument in Objekten und deren Abhängigkeiten untereinander darzustellen, so dass man einfach einzelne Elemente ersetzen kann. (Das erste mal gesehen habe ich sowas beim JDOM-Projekt im Java-Bereich, welches Zugriff auf XML-Dateien auf diese Weise wunderbar realisiert. Wollte so etwas eventuell mit einer LUA-Skript-Datei ebenfalls tun.)

    Ich hab jetzt nochmal darüber nachgedacht, was ich überhaupt *will* und hab mir nen einfachen iterativen Algorithmus, der von RegEx gar keinen gebrauch macht, zusammengebastelt:

    VB.NET-Quellcode

    1. input = input.Remove(0, input.IndexOf("VarScope(""mapname"")"))
    2. input = input.Remove(0, input.IndexOf("{"))
    3. Dim brackets As Integer = 0
    4. For i As Integer = 0 To input.Length - 1
    5. If input(i) = "{" Then
    6. brackets += 1
    7. ElseIf input(i) = "}" Then
    8. brackets -= 1
    9. End If
    10. If brackets = 0 Then
    11. input = input.Substring(1, i)
    12. Exit For
    13. End If
    14. Next

    Das findet mir den Bereich zwischen einem Start-Tag (hier hardcoded VarScope("mapname")) und dem zugehörigen End-Tag. Die einzelnen Zahlenwerte in den Value-Tags finde ich aus diesem Block dann mit RegEx raus. Für diesen Fall ist sowas denke ich einfach einfacher, RegEx ist für diese spezielle Sache offenbar nicht ganz so geeignet, wie ihr ja auch meint.
    Danke für alle Antworten und die Mühe :)
    Wegen RegEx... Den Algorithmus, der mir den Bereich zwischen nem Start- und End-Tag findet, habe ich auch über RegEx versucht zu implementieren, bin aber daran gescheitert, dass man in RegEx offenbar keine bereits geschriebenen Bereiche wiederverwenden kann - und so Schachtelungen erreichen kann. Das wäre notwendig, wenn in dem zu suchenden Klammerblock mehr als ein weiterer Klammernblock verschachtelt existiert. Wenn sich hier jmd mit EBNF auskennt: In ner normalen EBNF hätte man schreiben können (Definition für einen Klammernblock := K): S = K | SK Und hätte somit beliebig viele Verschachtelungen erreicht. Inwiefern kann RegEx dann den Anspruch erheben, formale Sprachen darzustellen? Oder gibts für solche Verschachtelungen vielleicht doch ne Funktion in RegEx, die mir nicht bewusst war?


    Noch
    übrigenser: Zum Testen von RegEx-Patterns empfehle ich den RegEx-Tester
    von ErfinderDesRades: [VB 2010] Regextester - OpenSource
    Werde ich mir merken ;) Vorausgesetzt ich beschäftige mich mal wieder mit RegEx...


    EDIT: Der VB-Parser spinnt... Oder er kommt mit "" nicht zurecht.