Stringmasken mit RegEx auflösen...

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
Ghosty1967
Beiträge: 205
Registriert: 29.08.2005 13:56
Computerausstattung: Intel i7, 128GB Ram, Win10 Ultimate, PB6.00 Alpha 3
Wohnort: Köln

Stringmasken mit RegEx auflösen...

Beitrag von Ghosty1967 »

Hallo zusammen,
Ich habe mir mal für ein kleines Projekt ein zwei Prozeduren gebastelt, die per übergebener Maske einen String neu zusammen- bzw. umbauen.
Das funktioniert eigentlich gut, ich glaube aber, das man genau für einen solchen Zweck die "RegularExpressions" nutzen könnte. Ich bin tatsächlich
nicht gerade Lesefaul und glaube auch, das ich ein gewisses Maß an logischen Denkvermögen besitze...aber bei der Beschreibung zu RegEx bin ich
dann doch raus. Ist von Euch Künstlern jemand anhand meines kleine Codeschnipsels in der Lage die gezeigten Prozeduren mit RegEx zu realisieren?

Hier der Beispielcode. In diesem habe ich jetzt nur zwei Prozeduren gepackt, es existieren noch weitere, aber die sollen erstmal eine
Herausforderung für mich bleiben um mit eurer Hilfe etwas zu lernen... :lol:

Code: Alles auswählen

Global NewList Filename.s()                                                               ;Fix mal eben eine Datenliste zu Testzwecken angelegt
AddElement(Filename()): Filename() = "Nirvana - From The Muddy Banks Of The Whiskah - 01. Intro.mp3"
AddElement(Filename()): Filename() = "Nirvana - From The Muddy Banks Of The Whiskah - 02. School.mp3"
AddElement(Filename()): Filename() = "Nirvana - From The Muddy Banks Of The Whiskah - 03. Drain You.mp3"
AddElement(Filename()): Filename() = "Nirvana - From The Muddy Banks Of The Whiskah - 04. Aneurysm.mp3"
AddElement(Filename()): Filename() = "Nirvana - From The Muddy Banks Of The Whiskah - 05. Smells Like Teen Spirit.mp3"
AddElement(Filename()): Filename() = "Nirvana - From The Muddy Banks Of The Whiskah - 06. Been A Son.mp3"
AddElement(Filename()): Filename() = "Nirvana - From The Muddy Banks Of The Whiskah - 07. Lithium.mp3"
AddElement(Filename()): Filename() = "Nirvana - From The Muddy Banks Of The Whiskah - 08. Sliver.mp3"
AddElement(Filename()): Filename() = "Nirvana - From The Muddy Banks Of The Whiskah - 09. Spank Thru.mp3"
AddElement(Filename()): Filename() = "Nirvana - From The Muddy Banks Of The Whiskah - 10. Scentless Apprentice.mp3"
AddElement(Filename()): Filename() = "Nirvana - From The Muddy Banks Of The Whiskah - 11. Heart-Shaped Box.mp3"
AddElement(Filename()): Filename() = "Nirvana - From The Muddy Banks Of The Whiskah - 12. Milk It.mp3"
AddElement(Filename()): Filename() = "Nirvana - From The Muddy Banks Of The Whiskah - 13. Negative Creep.mp3"
AddElement(Filename()): Filename() = "Nirvana - From The Muddy Banks Of The Whiskah - 14. Polly.mp3"
AddElement(Filename()): Filename() = "Nirvana - From The Muddy Banks Of The Whiskah - 15. Breed.mp3"
AddElement(Filename()): Filename() = "Nirvana - From The Muddy Banks Of The Whiskah - 16. Tourette's.mp3"
AddElement(Filename()): Filename() = "Nirvana - From The Muddy Banks Of The Whiskah - 17. Blew.mp3"

Procedure.s EvaluateN(MasterString.s, UserMask.s)
  nCount.i = CountString(UserMask, "[N")                                                  ;Zählen, wie oft der Platzhalter in der Maske vorkommt...
  If nCount > 0                                                                           ;Kommt der Platzhalter vor, dann...
    For loop = 1 To nCount                                                                ;Schleife aufrufen, bis alle Platzhalter abgearbeitet sind
      n_s.i = FindString(UserMask, "[N",   1, #PB_String_CaseSensitive)                   ;Startposition...
      n_e.i = FindString(UserMask, "]" , n_s, #PB_String_CaseSensitive)                   ;Endposition...
      n_l.i = (n_e - n_s) + 1                                                             ;und Länge des Platzhalters ermitteln...
      placeholder.s = Mid(UserMask, n_s, n_l)                                             ;und diesen aus der Maske kopieren und zwischenspeichern.
      If FindString(placeholder, "-") = 3                                                 ;Wenn für Zeichenanfang nur [N- steht und für Zeichenende eine Zahl angegeben ist,
        charStart.i = 1                                                                     ;...dann ist charStart immer 1
        charEnd.i   = Val(Mid(placeholder, 4, Len(placeholder) - 4))                        ;...und charEnd immer der angegebene Zahlenwert nach dem "-" Zeichen.
      ElseIf FindString(placeholder, "-") = Len(placeholder) - 1                          ;Wenn für Zeichenanfang eine Zahl angegeben ist und für Zeichenende nichts [N1-],
        charStart.i = Val(Mid(placeholder, 3, Len(placeholder) - 3))                        ;...dann ist charStart der Wert vor dem "-"
        charEnd.i   = 99999                                                                 ;...und charEnd thoretisch unendlich.
      ElseIf FindString(placeholder, "-")                                                 ;Wenn für Zeichenanfang und für Zeichenende eine Zahl angegeben ist,
        charStart.i = Val(Mid(placeholder, 3, FindString(placeholder, "-") - 3))            ;...dann ist charStart der Zahlenwert vor dem "-" Zeichen
        charEnd.i   = Val(Mid(placeholder, FindString(placeholder, "-") + 1, (FindString(placeholder, "]", 1) - (FindString(placeholder, "-") + 1)))) ;...und CharEnd ist  der Zahlenwert nach dem "-" Zeichen
      Else                                                                                ;Wenn nur eine Zahl nach dem Platzhalter angegebn ist,
        charStart.i = Val(Mid(placeholder, 3, FindString(placeholder, "]") - 1))            ;...dann entsprechen charStart
        charEnd.i   = Val(Mid(placeholder, 3, FindString(placeholder, "]") - 1))            ;...und charEnd diesem Zahlewert
      EndIf
      If charStart = 0: charStart = 1    : EndIf                                            ;Wenn charStart einen Zahlenwert von "0" hat, muss dieser immer auf 1 gesetzt werden.
      If charEnd   = 0: charEnd   = 99999: EndIf                                            ;Wenn charEnd einen Zahlenwert von "0" hat, muss dieser immer auf komplette Länge (unendlich) gesetzt werden.
      insertString.s = Mid(MasterString, charStart, (charEnd - charStart) + 1)              ;Generiert den Teilstring aus dem Ursprungsstring...
      UserMask = ReplaceString(UserMask, placeholder, insertString, #PB_String_NoCase, 1, 1)  ;...und ersetzt den Platzhalter in der Maske mit diesem,
    Next                                                                                  ;und den nächsten Platzhalter bearbeiten
    ProcedureReturn UserMask                                                              ;Die bearbeitete Maske zurückgeben
  EndIf
EndProcedure

Procedure.s EvaluateW(MasterString.s, UserMask.s)
  nCount.i = CountString(UserMask, "[W")                                                  ;Zählen, wie oft der Platzhalter in der Maske vorkommt...
  If nCount > 0                                                                           ;Kommt der Platzhalter vor, dann...
    For loop = 1 To nCount                                                                ;Schleife aufrufen, bis alle Platzhalter abgearbeitet sind
      n_s.i = FindString(UserMask, "[W",   1, #PB_String_CaseSensitive)                   ;Startposition...
      n_e.i = FindString(UserMask, "]" , n_s, #PB_String_CaseSensitive)                   ;Endposition...
      n_l.i = (n_e - n_s) + 1                                                             ;und Länge des Platzhalters ermitteln...
      placeholder.s = Mid(UserMask, n_s, n_l)                                             ;und diesen aus der Maske kopieren und zwischenspeichern.
      InsertString.s = ""                                                                 ;Da hier evtl. mehrere Durchläufe vorkommen können muss InsertString jedesmal gelöscht werden...
      If FindString(placeholder, "-") = 3                                                 ;Wenn für das erste Wort nur [W- steht und für das letzte Wort eine Zahl angegeben ist,
        wordStart.i = 1                                                                     ;...dann ist wordStart immer 1
        wordEnd.i   = Val(Mid(placeholder, 4, Len(placeholder) - 4))                        ;...und wordEnd immer der angegebene Zahlenwert nach dem "-" Zeichen.
      ElseIf FindString(placeholder, "-") = Len(placeholder) - 1                          ;Wenn für das erste Wort eine Zahl angegeben ist und für Zeichenende nichts [W1-],
        wordStart.i = Val(Mid(placeholder, 3, Len(placeholder) - 3))                        ;...dann ist wordStart der Wert vor dem "-"
        wordEnd.i   = CountString(MasterString, " ") + 1                                    ;...und wordEnd die Anzahl der Trennzeichen + 1
      ElseIf FindString(placeholder, "-")                                                 ;Wenn für das erste und das letzte Wort eine Zahl angegeben ist,
        wordStart.i = Val(Mid(placeholder, 3, FindString(placeholder, "-") - 3))            ;...dann ist das erste Wor der Zahlenwert vor dem "-" Zeichen
        wordEnd.i   = Val(Mid(placeholder, FindString(placeholder, "-") + 1, (FindString(placeholder, "]", 1) - (FindString(placeholder, "-") + 1)))) ;...und wordEnd ist der Zahlenwert nach dem "-" Zeichen
      Else                                                                                ;Wenn nur eine Zahl nach dem Platzhalter angegebn ist,
        wordStart.i = Val(Mid(placeholder, 3, FindString(placeholder, "]") - 1))            ;...dann entsprechen wordStart]
        wordEnd.i   = Val(Mid(placeholder, 3, FindString(placeholder, "]") - 1))            ;...und wordEnd diesem Zahlenwert.
      EndIf  
      If wordStart = 0: wordStart = 1: EndIf                                              ;Wenn cwordStart einen Zahlenwert von "0" hat, muss dieser immer auf 1 gesetzt werden.
      If wordEnd   = 0: wordEnd = CountString(MasterString, " ") + 1: EndIf               ;Wenn wordEnd einen Zahlenwert von "0" hat, muss dieser immer auf die Anzahl der Trennzeichen + 1 gesetzt werden
      For i = wordStart To wordEnd                                                        ;Starten der Schleife um alle Teilstrings aus dem Ursprungsstring zu holen und zusammenzufügen.
        insertString.s + StringField(MasterString, i, " ") + " "                          ;...zusammenfügen des einzufügenden Strings
      Next i                                                                              ;nächstes Wort bearbeiten.
      UserMask = ReplaceString(UserMask, placeholder, insertString, #PB_String_NoCase, 1, 1)  ;...ersetzten des Platzhalters in der Maske mit den generierten String,
    Next                                                                                  ;und den nächsten Platzhalter bearbeiten
    ProcedureReturn UserMask                                                              ;Die bearbeitete Maske zurückgeben.
  EndIf
EndProcedure



Debug "Vor der Bearbeitung..."
ForEach Filename(): Debug Filename(): Next
;=========================================
mask.s = "[W11] Album: [W6] [W3] [W1] - Titelname: [W12-]"
If FindString(mask, "[N", 1, #PB_String_CaseSensitive)                                    ;Alle Filenamen nach der Maske [N] durchsuchen...
  ForEach Filename(): Filename() = EvaluateN(Filename(), mask): Next
EndIf
If FindString(mask, "[W", 1, #PB_String_CaseSensitive)                                    ;Alle Filenamen nach der Maske [W] durchsuchen...
  ForEach Filename(): Filename() = EvaluateW(Filename(), mask): Next
EndIf
;=========================================
Debug ""
Debug "Nach der Bearbeitung..."
ForEach Filename(): Debug Filename(): Next
Vielleicht noch eine schnelle Erklärung...
Mit Hilfe einer übergebenen Maske z.B. "[N15-18], [N5-9]" und einem ebenfalls übergebenen Anfangsstring z.B. "PUREBASIC IST TOLL" würde dann "TOLL, BASIC" zurückgegeben, weil...
es wird aus dem Anfangsstring der Teilstring [N15-18] (Buchstabe 15-18) geholt und mit dem Platzhalter der Maske ersetzt, dann das ", " eingesetzt und schließlich der Teilstring [N5-9] (Buchstabe 5-9)
geholt und mit dem Platzhalter der Maske ersetzt
:shock: :freak:
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8807
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Re: Stringmasken mit RegEx auflösen...

Beitrag von NicTheQuick »

Ich habe deine Ersetzung nicht ganz verstanden, z.B. verstehe ich nicht warum aus "From The Muddy Banks Of The Whiskah" nur noch "Banks" übrig bleiben soll. Ich habe dir hier aber mal ein interaktives Beispiel bei regex101 gebastelt: https://regex101.com/r/TLnJZD/1

Da kannst du erst mal üben wie das mit RegEx am besten zu machen ist und es dann relativ leicht auf Purebasic übertragen. Es wird dir dort auch detailliert erklärt was jedes einzelne Zeichen zu bedeuten hat und was es tut.

Aber du kannst natürlich Regular Expressions in Purebasic erst mal nur dafür verwenden den Ursprungsstring zu splitten und dann die einzelnen RegEx-Gruppen nochmal mit den üblichen Stringbefehlen bearbeiten und zurecht schneiden bevor du alles wieder in einen Zielstring zusammensetzt.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8807
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Re: Stringmasken mit RegEx auflösen...

Beitrag von NicTheQuick »

Noch ein anderer Tipp zu deinem Coding-Stil:
In Procedures nutzt du am besten immer das Schlüsselwort `Protected` um lokale Variablen zu deklarieren. Als Beispiel nehme ich mal `charStart.i`. Du hast die Variable in deinem Code an mehreren Stellen deklariert, indem du überall `.i` angehängt hast, wo sie laut Programmfluss das erste mal vorkommt. Sauberer ist es die Variable am Anfang der Procedure einmalig mit `Protected charStart.i` zu deklarieren. Damit ist sichergestellt, dass du nicht aus Versehen eine globale Variable, die genauso heißt, nutzt. Und außerdem hast du die Typdeklaration dann nur einmal im Code.

Damit du das nie vergessen kannst, schreibst du am Anfang deines Codes einfach einmalig `EnableExplicit`. Dann meckert der Compiler über jede nicht sauber deklarierte Variable, Funktion und Co.
Benutzeravatar
Ghosty1967
Beiträge: 205
Registriert: 29.08.2005 13:56
Computerausstattung: Intel i7, 128GB Ram, Win10 Ultimate, PB6.00 Alpha 3
Wohnort: Köln

Re: Stringmasken mit RegEx auflösen...

Beitrag von Ghosty1967 »

Es ist schon ein wenig doof zu erklären aber soviel sei gesagt, "Banks" bleibt übrig, da ich in meinem Beispiel in der Maske nach [W6] frage und das sechste Wort in
"Nirvana - From The Muddy Banks Of The Whiskah - 05. Smells Like Teen Spirit.mp3" ist Banks (das Minuszeichen zählt auch als Wort)
Nehmen wir an:
Originalstring = "Nirvana - From The Muddy Banks Of The Whiskah - 05. Smells Like Teen Spirit.mp3""
Maske = "[W11] Album: [W6] [W3] [W1] - Titelname: [W12-]"

1. Die Maske möchte als erstes für den Platzhalter [W11] das 11. Wort aus dem Originalstring haben um diesen Platzhalter damit zu ersetzen.
Dann sähe die Maske danach so aus: "01. Album: [W6] [W3] [W1] - Titelname: [W12-]"
2. Dann folgt in der Maske ein normaler Text, welcher einfach weiter übernommen wird, nämlich " Album: ".
Also sieht die Maske danach so aus: "01. Album: [W6] [W3] [W1] - Titelname: [W12-]"
3. Jetzt folgt der nächste Platzhalter [W6] also das 6. Wort aus dem Originalstring, welches "Banks" lautet (das "-" zählt auch als Wort)
und schon wird aus der Maske: "01. Album: Banks [W3] [W1] - Titelname: [W12-]"
3. [W3] ist nun an der Reihe und wird zu einem "From"
und aus der Maske wird: "01. Album: Banks From [W1] - Titelname: [W12-]"
4. [W1] wird auch noch einfach eingetauscht gegen ein "Nirvana"
und die Maske wird jetzt zu "01. Album: Banks From Nirvana - Titelname: [W12-]"
5. Jetzt folgt in der Maske wieder ein normaler Text, welcher auch einfach weiter übernommen wird, nämlich " - Titelname: ".
und aus der Maske wird: "01. Album: Banks From Nirvana - Titelname: [W12-]"
6. Nunsteht nur noch das [W12-] in der Maske. Dabei bedeutet das Minus hinter der 12, das alle folgenden Wörter mitgenommen werden und für [W12-] eingetauscht werden sollen.
Dann wird endlich aus "[W11] Album: [W6] [W3] [W1] - Titelname: [W12-]" das Ergebnis "05. Album: Banks From Nirvana - Titelname: Smells Like Teen Spirit.mp3"

Ich weiss, das ist echt ein bisschen spooky zu erklären aber so müsste es gehen.
@NicTheQuick: Übrigens versuche ich schon lange deine oben genannten Tipps durchzusetzten, aber ich bekomme meinen Uralt-Stil einfach nicht abtrainiert
Antworten