VBA Tipp: Reguläre Ausdrücke verwenden

Aus DBWiki
Wechseln zu: Navigation, Suche

Mit Regulären Ausdrücken lassen sich viele Aufgaben mit geringem Aufwand lösen, für die bei normaler Programmierung ein Vielfaches an Anweisungen erforderlich wären. Hier sind einige typische Anwendungsfälle genannt:

  • Kommandozeilenüberprüfung
  • Parsen von Benutzereingaben
  • Parsen unterschiedlicher Textdateien
  • Prüfen von Log-Protokollen
  • Textsuche in E-Mails
  • Lesen von Konfigurationsdateien
  • Validieren diverser Eingabeformate wie Telefonnummern, E-Mail-Adressen, Kreditkartennummern, Datumsformaten, u.v.a.


In VBA stehen Reguläre Ausdrücke über einen Verweis auf die VBScript-Bibliothek als RegExp-Objekt zur Verfügung. Die Implementierung weist gegenüber dem Quasi-Standard PCRE einige kleine Lücken auf, die hier angeführt sind:

  • Keine Anker, die zum Anfang oder Ende der Zeichenkette passen. (Benutze stattdessen ^ und $)
  • Kein Lookbehind.
  • Keine atomare Gruppierung oder besitzergreifenden Quantifizierer
  • Keine Unicode-Unterstützung, außer für den Abgleich einzelner Zeichen z.B. mittels \uFFFFFF
  • Keine benannten Sammelgruppen. (Benutze stattdessen nummerierte Sammelgruppen.)
  • Keine Modifikatoren zum Setzen von Anpassungsoptionen innerhalb des regulären Ausdrucks.
  • Keine Bedingungen.
  • Keine Kommentierung.


Im Suchmuster (Pattern) können folgende Metazeichen verwendet werden:

Zeichen Beschreibung
\ Markiert das nächste Zeichen entweder als Sonderzeichen oder als Literal. Beispielsweise entspricht n dem Zeichen n. \n passt zu einem Zeilenumbruch. Die Sequenz \\ passt zu \ und \( passt zu (.
^ Entspricht dem Beginn einer Eingabe.
$ Entspricht dem Ende einer Eingabe.
* Stimmt mit dem vorhergehenden Zeichen kein- oder mehrmalig überein. Beispielsweise passt zo* entweder zu z oder zoo.
+ Stimmt mit dem vorhergehenden Zeichen ein- oder mehrmalig überein. Zum Beispiel passt zo+ zu zoo, aber nicht zu z.
? Entspricht dem vorhergehenden Zeichen kein- oder einmalig. Zum Beispiel, a?be? entspricht dem be in Leber.
. Passt auf jedes einzelne Zeichen außer einem Zeilenumbruchzeichen.
(muster) Stimmt mit dem Muster überein und merkt sich die Übereinstimmung. Der passende Teilstring kann aus der resultierenden Match-Collection mit Item [0]..[n] abgerufen werden. Um Klammern () zu verwenden, müssen diese als \( oder \) ausgewiesen sein.
x|y Entspricht entweder x oder y. Z.B. z|Holz entspricht z oder Holz. (L|N)ager passt zu Lager oder Nager.
{n} n ist eine nichtnegative ganze Zahl. Stimmt genau n-mal überein. Zum Beispiel stimmt o{2} nicht mit dem o in Lob überein, sondern mit den ersten beiden o nach dem d in "Wodoo".
{n,} n ist eine nichtnegative ganze Zahl. Entspricht mindestens n-mal. Zum Beispiel stimmt o{2,} nicht mit dem o in Lob überein und stimmt mit allen o hinter dem d in Wodoooo überein. o{1,} entspricht o+. o{0,} entspricht o*.
{n,m} m und n sind nichtnegative ganze Zahlen. Entspricht mindestens n und höchstens m mal. Zum Beispiel, o{1,3} passt zu den ersten drei o in "achsoooo". o{0,1} entspricht o?.
[xyz] Ein Zeichenvorrat. Stimmt mit einem der eingeschlossenen Zeichen überein. Zum Beispiel entspricht [abc] dem a in Hafen.
[^xyz] Ein negativer Zeichenvorrat. Stimmt mit jedem Zeichen überein, das nicht eingeschlossen ist. Zum Beispiel entspricht [^abc] dem H in "Hafen".
[a-z] Eine Reihe von Zeichen. Stimmt mit einem beliebigen Zeichen im angegebenen Bereich überein. Beispielsweise passt [a-z] zu jedem Kleinbuchstaben im Bereich a bis z.
[^m-z] Ein negativer Zeichenbereich. Stimmt mit jedem Zeichen überein, das nicht im angegebenen Bereich liegt. Beispielsweise passt [m-z] auf jedes Zeichen, das nicht im Bereich von m bis z liegt.
\b Entspricht einer Wortgrenze, d.h. der Position zwischen einem Wort und einem Leerzeichen. Zum Beispiel entspricht er\b dem er in Leber, aber nicht dem er in Lebertran.
\B Stimmt mit einer Nicht-Wortgrenze überein. gu\B passt zum gu in "sehr gut".
\d Stimmt mit einem Ziffernzeichen überein. Entspricht [0-9].
\D Stimmt nicht mit einem Ziffernzeichen überein. Entspricht [^0-9].
\f Entspricht einem Formularvorschubzeichen.
\n Entspricht einem Zeilenvorschubzeichen.
\r Stimmt mit einem Wagenrücklaufzeichen überein.
\s Passt zu jedem beliebigen Leerzeichen, einschließlich Leerzeichen, Tabulator, Formularvorschub usw. Entspricht [ \f\n\r\t\v].
\S Passt auf jedes beliebige Leerzeichen. Entspricht [^ \f\n\r\t\v].
\t Entspricht einem Tabulatorzeichen.
\v Stimmt mit einem vertikalen Tabulatorzeichen überein.
\w Stimmt mit einem beliebigen Wortzeichen einschließlich Unterstrich überein. Entspricht [A-Za-z0-9_].
\W Stimmt mit einem beliebigen Nicht-Wort Zeichen überein. Entspricht [^A-Za-z0-9_].
\num Entspricht num, wobei num eine positive ganze Zahl ist. Eine Referenz zurück zu den gespeicherten Übereinstimmungen. Zum Beispiel stimmt (.)\1 mit zwei aufeinanderfolgenden identischen Zeichen überein.
\n Entspricht n, wobei n ein oktaler Escape-Wert ist. Oktale Escape-Werte müssen 1, 2 oder 3 Stellen lang sein. Zum Beispiel, \11 und \011 passen beide zu einem Tabulatorzeichen. \0011 ist das Äquivalent zu \001 & 1. Oktale Fluchtwerte dürfen 256 nicht überschreiten. Ist dies der Fall, werden nur die ersten beiden Ziffern des Ausdrucks verwendet. Ermöglicht die Verwendung von ASCII-Codes in regulären Ausdrücken.
\xn Entspricht n, wobei n ein hexadezimaler Escape-Wert ist. Hexadezimale Escape-Werte müssen genau zweistellig sein. Zum Beispiel, \x41 passt zu A. \x041 entspricht \x04 & 1. Ermöglicht die Verwendung von ASCII-Codes in regulären Ausdrücken.

VBA-Modul

Option Explicit
 
Private RegEx As Object
 
Private Property Get rx() As Object
   If RegEx Is Nothing Then Set RegEx = CreateObject("VBScript.RegExp")
   Set rx = RegEx
End Property
 
' Falls man unbedingt den Speicher freigeben muss.
Public Sub ResetRegEx()
   Set RegEx = Nothing
End Sub
 
' Flags: "i":ignore case und "g":global, "m":multiline.
' Gibt True zurück, wenn eine Übereinstimmung gefunden wurde, andernfalls False.
Public Function rxTest(Source As Variant, Pattern As String, _
                       Optional Flags As String) As Boolean
   If IsNull(Source) Or IsEmpty(Source) Then Exit Function
 
   With rx
      .IgnoreCase = CBool(InStr(1, Flags, "i") > 0)
      .Global = CBool(InStr(1, Flags, "g") > 0)
      .MultiLine = CBool(InStr(1, Flags, "m") > 0)
      .Pattern = Pattern
      rxTest = .Test(Source)
   End With
End Function
 
' Flags: "i":ignore case und "g":global, "m":multiline.
' Gibt den geänderten String zurück, wobei der Eingabestring unverändert bleibt.
Public Function rxReplace(Source As Variant, Pattern As String, _
                          ReplaceString As String, _
                          Optional Flags As String) As Variant
   rxReplace = Null
   If IsNull(Source) Or IsEmpty(Source) Then Exit Function
 
   With rx
      .IgnoreCase = CBool(InStr(1, Flags, "i") > 0)
      .Global = CBool(InStr(1, Flags, "g") > 0)
      .MultiLine = CBool(InStr(1, Flags, "m") > 0)
      .Pattern = Pattern
      rxReplace = .Replace(Source, ReplaceString)
   End With
End Function
 
' Flags: "i":ignore case und "g":global, "m":multiline.
' Gibt eine Matches-Collection zurück, wenn es Treffer gibt oder sonst Nothing.
Public Function rxExec(Source, Pattern$, Optional Flags$) As Object
   If IsNull(Source) Or IsEmpty(Source) Then Exit Function
 
   With rx
      .IgnoreCase = CBool(InStr(1, Flags, "i") > 0)
      .Global = CBool(InStr(1, Flags, "g") > 0)
      .MultiLine = CBool(InStr(1, Flags, "m") > 0)
      .Pattern = Pattern
      Set rxExec = .Execute(Source)
   End With
End Function
 
' Flags: "i":ignore case und "g":global, "m":multiline.
' Teilt Source an den Positionen in ein Stringarray auf, das durch Pattern
' definiert ist.
Public Function rxSplit(Source As Variant, Pattern As String, _
                        Optional Flags As String = "gm") As String()
   If IsNull(Source) Or IsEmpty(Source) Then Exit Function
 
   rxSplit = Split(rxReplace(Source, Pattern, vbNullChar, Flags), vbNullChar)
End Function

Beispiele

Es nur alphanumerische Zeichen, Leerzeichen und Bindestriche in einem String erhalten bleiben. Dafür lässt sich das Muster [^a-zA-Z0-9 -] verwenden, welches global (also mehrmals) zur Ersetzung durch eine leere Zeichenkette angewendet wird.


Im VBA-Direktfenster:

?rxReplace("$""§&Ha//\\l\}l!!o €D@|B´`|W<>,i..k#~i][²²", "[^a-zA-Z0-9 -]", vbNullString, "g")
Hallo DBWiki


Es soll ein String, der durch beliebige Whitespace-Zeichen getrennt ist, in seine alphanumerischen Teile aufgetrennt werden.

Im VBA-Direktfenster:

v = "A  " & vbLf & "  B" & vbTab & Chr$(9) & "   C"
s = rxSplit(v, "\s+")
?Join(s, vbCrLf)
A
B
C


Es sollen alle Ziffern aus einer Zeichenfolge extrahiert werden.

Im VBA-Direktfenster: (Lösung 2 aus VBA Tipp: Ziffern aus Zeichenfolge extrahieren ist fast doppelt so schnell.)

?rxReplace("a20bc 20am16 aaa25rt", "\D", vbNullString, "g")
20201625


So werden einzelne Wörter gefunden, die von Apostrophen umgeben sind.

Public Sub rxBeispiel()
   Const Text     As String = "Andrea fragte mich: " & _
                              """Hast du Grass' 'Blechtrommel' gelesen?"""
   Const Pattern1 As String = "'\w+?'"
   Const Pattern2 As String = "'(\w+?)'"
   Dim m
 
   If rxTest(Text, Pattern1) Then
      Set m = rxExec(Text, Pattern1)
      Debug.Print m(0)                '==> 'Blechtrommel'
   End If
 
   If rxTest(Text, Pattern2) Then
      Set m = rxExec(Text, Pattern2)
      Debug.Print m(0).SubMatches(0)  '==> Blechtrommel
   End If
End Sub


Weitere Beispiele folgen später.

Weblinks