{"id":55001617,"date":"2026-10-01T00:00:00","date_gmt":"2026-10-05T19:31:52","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=1617"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/","title":{"rendered":"Regul&auml;re Ausdr&uuml;cke in VBA, Teil 2: Praxis-Rezepte"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg01.met.vgwort.de\/na\/9b2f4c578aaf4742884daa81fe9c552f\" width=\"1\" height=\"1\" alt=\"\"><b>Im ersten Teil dieser Reihe haben wir das RegExp-Objekt eingebunden, seine vier Klassen kennengelernt und die Mustersprache durchgearbeitet. Nun wird es konkret. Dieser Beitrag liefert fertige Muster f&uuml;r die Pr&uuml;fung von E-Mail-Adressen, Telefonnummern, Postleitzahlen und Kfz-Kennzeichen, dazu Funktionen zum Herausl&ouml;sen von Daten aus Freitextfeldern, zum Bereinigen kopierter Texte und zum F&uuml;llen von Textvorlagen. Alle Beispiele setzen die drei Hilfsfunktionen aus dem ersten Teil voraus &#8211; und am Ende zeigen wir eine Falle, die eine Access-Anwendung zuverl&auml;ssig zum Stillstand bringt.<\/b><\/p>\n<h2>Muster geh&ouml;ren in Konstanten<\/h2>\n<p>Bevor wir loslegen, eine organisatorische Empfehlung: Legen Sie ein Standardmodul an, beispielsweise namens <b>mdlRegExPatterns<\/b>, in dem alle Muster als Konstanten stehen (siehe Listing 1). Der Grund ist weniger die Schreibarbeit als die Wartung &#8211; wenn sich herausstellt, dass Ihr Muster f&uuml;r Telefonnummern eine Schreibweise nicht erkennt, wollen Sie das an genau einer Stelle korrigieren und nicht an mehreren anderen Stellen.<\/p>\n<pre><span style=\"color:blue;\">Option Compare Database<\/span>\r\n<span style=\"color:blue;\">Option Explicit<\/span>\r\n<span style=\"color:blue;\">Public <\/span>Const cstrRegExEMail<span style=\"color:blue;\"> As String<\/span> = _\r\n    \"^[A-Za-z0-9_%+\\-]+(?:\\.[A-Za-z0-9_%+\\-]+)*\" & _\r\n    \"@[A-Za-z0-9](?:[A-Za-z0-9\\-]*[A-Za-z0-9])?\" & _\r\n    \"(?:\\.[A-Za-z0-9](?:[A-Za-z0-9\\-]*[A-Za-z0-9])?)+$\"\r\n<span style=\"color:blue;\">Public <\/span>Const cstrRegExPlzDe<span style=\"color:blue;\"> As String<\/span> = \"^\\d{5}$\"\r\n<span style=\"color:blue;\">Public <\/span>Const cstrRegExIban<span style=\"color:blue;\"> As String<\/span> = _\r\n    \"^[A-Z]{2}\\d{2}[A-Z0-9]{11,30}$\"\r\n<span style=\"color:blue;\">Public <\/span>Const cstrRegExKennzeichen<span style=\"color:blue;\"> As String<\/span> = _\r\n    \"^[A-Z&Auml;&Ouml;&Uuml;]{1,3}-[A-Z&Auml;&Ouml;&Uuml;]{1,2} ?\\d{1,4}[EH]?$\"\r\n<span style=\"color:blue;\">Public <\/span>Const cstrRegExUrl<span style=\"color:blue;\"> As String<\/span> = _\r\n    \"^https?:\/\/[A-Za-z0-9]\" & _\r\n    \"(?:[A-Za-z0-9\\-]*[A-Za-z0-9])?\" & _\r\n    \"(?:\\.[A-Za-z0-9]\" & _\r\n    \"(?:[A-Za-z0-9\\-]*[A-Za-z0-9])?)+\" & _\r\n    \"(?::\\d{1,5})?(?:[\/#?][^\\s]*)?$\"<\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Die Muster gesammelt an einer Stelle<\/span><\/b><\/p>\n<p>Beachten Sie den maskierten Bindestrich in den Zeichenklassen. Innerhalb eckiger Klammern hat der Bindestrich die Bedeutung eines Bereichs, weshalb wir ihm mit einem vorangestellten Schr&auml;gstrich diese Rolle nehmen. Alternativ d&uuml;rfen Sie ihn auch unmaskiert an die letzte Position der Klasse setzen.<\/p>\n<p>Die Muster pr&uuml;fen jeweils nur den formalen Aufbau. Bei einer IBAN werden weder die l&auml;nderspezifische L&auml;nge noch die Pr&uuml;fziffer kontrolliert. Auch das Kennzeichenmuster deckt bewusst nur die &uuml;blichen deutschen Standard-, Elektro- und Historienkennzeichen ab. Saison-, Wechsel- und Sonderkennzeichen ben&ouml;tigen eigene Erweiterungen.<\/p>\n<h2>Rezept 1: E-Mail-Adressen pr&uuml;fen<\/h2>\n<p>Vorweg eine Einordnung, die Ihnen sp&auml;ter &Auml;rger erspart: Ein regul&auml;rer Ausdruck, der wirklich jede nach den Internetstandards zul&auml;ssige E-Mail-Adresse korrekt beurteilt, ist mehrere tausend Zeichen lang und in der Praxis kaum sinnvoll einzusetzen.<\/p>\n<p>Ein pragmatisches Muster ist deshalb meist die bessere Wahl: Es f&auml;ngt grobe Eingabefehler wie ein fehlendes At-Zeichen oder einen vergessenen Punkt in der Domain ab, bleibt aber verst&auml;ndlich und wartbar.<\/p>\n<p>Was ein Muster grunds&auml;tzlich nicht leisten kann, ist die Pr&uuml;fung, ob das Postfach tats&auml;chlich existiert. Verl&auml;sslich kl&auml;ren l&auml;sst sich das nur &uuml;ber eine Best&auml;tigungsnachricht.<\/p>\n<p>Mit dieser Einschr&auml;nkung im Hinterkopf eignet sich das Muster aus <b>cstrRegExEMail<\/b> gut als Plausibilit&auml;tspr&uuml;fung:<\/p>\n<pre><span style=\"color:blue;\">Debug.Print<\/span> RegExTest(\"andre@access-im-unternehmen.de\", _\r\n    cstrRegExEMail)\r\n<span style=\"color:blue;\">Debug.Print<\/span> RegExTest(\"andre@@example.de\", cstrRegExEMail)\r\n<span style=\"color:blue;\">Debug.Print<\/span> RegExTest(\"andre.example.de\", cstrRegExEMail)<\/pre>\n<p>Die Ausgabe im Direktbereich:<\/p>\n<pre>Wahr\r\nFalsch\r\nFalsch<\/pre>\n<p>In einem Formular setzen Sie die Pr&uuml;fung sinnvollerweise im Ereignis <b>Vor Aktualisierung<\/b> des Textfelds ein. Wichtig ist die vorherige Pr&uuml;fung auf einen leeren Wert: Ist die Eingabe optional, soll ein leergelassenes Feld nicht als ung&uuml;ltige E-Mail-Adresse beanstandet werden (siehe Listing 2).<\/p>\n<pre><span style=\"color:blue;\">Private Sub <\/span>txtEMail_BeforeUpdate(Cancel<span style=\"color:blue;\"> As Integer<\/span>)\r\n    <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(Nz(Me.txtEMail, \"\")) = 0<span style=\"color:blue;\"> Then<\/span> <span style=\"color:blue;\">Exit Sub<\/span>\r\n    <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> RegExTest(Me.txtEMail, cstrRegExEMail)<span style=\"color:blue;\"> Then<\/span>\r\n        <span style=\"color:blue;\">MsgBox<\/span> \"Bitte geben Sie eine g&uuml;ltige E-Mail-Adresse ein.\", vbExclamation\r\n        Cancel = <span style=\"color:blue;\">True<\/span>\r\n    <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 2: Pr&uuml;fung der E-Mail vor Aktualisierung des Steuerelements<\/span><\/b><\/p>\n<p>Eine Einschr&auml;nkung noch: In der G&uuml;ltigkeitsregel eines Tabellenfelds l&auml;sst sich diese Pr&uuml;fung nicht unterbringen, weil dort keine selbst geschriebenen Funktionen erlaubt sind. Sie geh&ouml;rt daher ins Formular &#8211; oder in eine Auswahlabfrage, mit der Sie den Bestand nachtr&auml;glich auf fehlerhafte Werte untersuchen.<\/p>\n<h2>Rezept 2: Telefonnummern pr&uuml;fen und vereinheitlichen<\/h2>\n<p>Telefonnummern sind der Klassiker unter den unsauberen Daten. In einer gewachsenen Tabelle finden sich Schreibweisen wie <b>0203\/1234567<\/b>, <b>+ 203 1234567<\/b>, <b>(0203) 12 34 56 7<\/b> und <b>0203-1234567<\/b> nebeneinander. Ein Muster, das alle diese Varianten erkennt und gleichzeitig Unsinn zur&uuml;ckweist, wird schnell un&uuml;bersichtlich.<\/p>\n<p>Der praxistauglichere Weg besteht aus zwei Schritten: Zuerst pr&uuml;fen wir grob auf erlaubte Zeichen, dann z&auml;hlen wir die Ziffern. Das ist robuster als ein einzelnes Monstermuster und deutlich leichter zu warten (siehe Listing 3).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>IstTelefonnummer(ByVal strNummer<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Boolean<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>strZiffern<span style=\"color:blue;\"> As String<\/span>\r\n    ' Nur erlaubte Zeichen?\r\n    <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> RegExTest(strNummer, \"^[\\d \\-\/\\(\\)\\+]+$\")<span style=\"color:blue;\"> Then<\/span> <span style=\"color:blue;\">Exit Function<\/span>\r\n    ' Wie viele Ziffern bleiben &uuml;brig?\r\n    strZiffern = RegExErsetzen(strNummer, \"\\D\", \"\")\r\n    IstTelefonnummer = (<span style=\"color:blue;\">Len<\/span>(strZiffern) &gt;= 6) And (<span style=\"color:blue;\">Len<\/span>(strZiffern) &lt;= 15)\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 3: Zweistufige Pr&uuml;fung einer Telefonnummer<\/span><\/b><\/p>\n<p>Die zweite Zeile der Funktion ist gleich noch f&uuml;r etwas anderes gut. Der Ausdruck <b>\\D<\/b> trifft alles, was keine Ziffer ist &#8211; und weil wir ihn durch einen Leerstring ersetzen, bleiben genau die Ziffern &uuml;brig.<\/p>\n<p>Auf dieser Idee baut die Vereinheitlichung auf, mit der wir alle Nummern in das internationale Format &uuml;berf&uuml;hren (siehe Listing 4).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>TelefonNormalisieren(ByVal strNummer<span style=\"color:blue;\"> As String<\/span>, <span style=\"color:blue;\">Optional<\/span> ByVal strLand<span style=\"color:blue;\"> As String<\/span> = \"\")<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>strZiffern<span style=\"color:blue;\"> As String<\/span>\r\n    strZiffern = RegExErsetzen(strNummer, \"\\D\", \"\")\r\n    <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Left<\/span>(strZiffern, 2) = \"00\"<span style=\"color:blue;\"> Then<\/span>\r\n        ' 00203... -&gt; 203...\r\n        strZiffern = <span style=\"color:blue;\">Mid<\/span>(strZiffern, 3)\r\n    <span style=\"color:blue;\">Else<\/span>If <span style=\"color:blue;\">Left<\/span>(strZiffern, 1) = \"0\" Then\r\n        ' 0203... -&gt; 203...\r\n        strZiffern = strLand & <span style=\"color:blue;\">Mid<\/span>(strZiffern, 2)\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    TelefonNormalisieren = \"+\" & strZiffern\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 4: Umwandlung in das internationale Format<\/span><\/b><\/p>\n<p>Ein Testlauf zeigt, dass die vier eingangs genannten Schreibweisen alle beim gleichen Ergebnis landen. Hier die Aufrufe:<\/p>\n<pre><span style=\"color:blue;\">Debug.Print<\/span> TelefonNormalisieren(\"0203\/1234567\")\r\n<span style=\"color:blue;\">Debug.Print<\/span> TelefonNormalisieren(\"+ 203 1234567\")\r\n<span style=\"color:blue;\">Debug.Print<\/span> TelefonNormalisieren(\"(0203) 12 34 56 7\")\r\n<span style=\"color:blue;\">Debug.Print<\/span> TelefonNormalisieren(\"0203-1234567\")<\/pre>\n<p>Das Ergebnis lautet einheitlich:<\/p>\n<pre>+2031234567\r\n+2031234567\r\n+2031234567\r\n+2031234567<\/pre>\n<p>Genau das brauchen Sie, wenn Sie Dubletten finden oder Nummern an eine Telefonanlage &uuml;bergeben wollen. Die Funktion setzt allerdings voraus, dass nationale Nummern mit einer f&uuml;hrenden Null und internationale Nummern mit <b>+<\/b> oder <b>00<\/b> gespeichert sind. Eine Nummer wie <b>2031234567<\/b> l&auml;sst sich ohne weitere Information keinem Land sicher zuordnen. Als Aktualisierungsabfrage &uuml;ber einen entsprechend gepr&uuml;ften Bestand r&auml;umt die Funktion in einem Durchgang auf.<\/p>\n<h2>Rezept 3: Daten aus Freitext herausl&ouml;sen<\/h2>\n<p>H&auml;ufiger als das Pr&uuml;fen ist das Extrahieren. In fast jeder gewachsenen Datenbank gibt es ein Bemerkungsfeld, in dem &uuml;ber Jahre Informationen gelandet sind, die eigentlich in eigene Felder geh&ouml;ren. Mit <b>RegExGruppe<\/b> aus dem ersten Teil holen wir sie heraus.<\/p>\n<p>Der Klassiker ist die Adresszeile, in der Postleitzahl und Ort zusammenstehen. Das Muster trennt beides und liefert je nach Parameter den einen oder den anderen Teil:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>PlzAusAdresse( _\r\n        ByVal strZeile<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n    PlzAusAdresse = RegExGruppe(strZeile, _\r\n        \"\\b(\\d{5})\\s+(.+)$\", 1)\r\n<span style=\"color:blue;\">End Function<\/span>\r\n<span style=\"color:blue;\">Public Function <\/span>OrtAusAdresse( _\r\n        ByVal strZeile<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n    OrtAusAdresse = RegExGruppe(strZeile, _\r\n        \"\\b(\\d{5})\\s+(.+)$\", 2)\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Ebenso oft gesucht: der Betrag in einem Text. Deutsche Betr&auml;ge sind mit ihrem Punkt als Tausendertrennzeichen und dem Komma als Dezimaltrenner etwas eigenwillig, aber gut zu fassen. Der erste Teil des Musters beschreibt die Variante mit Tausenderpunkten, der zweite die ohne (siehe Listing 5).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>BetragAusText(ByVal strText<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Variant<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>strTreffer<span style=\"color:blue;\"> As String<\/span>\r\n    strTreffer = RegExGruppe(strText, \"\\d{1,3}(?:\\.\\d{3})+,\\d{2}|\\d+,\\d{2}\", 0)\r\n    <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strTreffer) = 0<span style=\"color:blue;\"> Then<\/span>\r\n        BetragAusText = Null\r\n    <span style=\"color:blue;\">Else<\/span>\r\n        BetragAusText = CCur(strTreffer)\r\n    <span style=\"color:blue;\">End If<\/span>\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 5: Herausl&ouml;sen eines Geldbetrags<\/span><\/b><\/p>\n<p>Der Aufruf mit dem Text <b>Rechnungsbetrag 1.234,50 Euro netto<\/b> liefert den W&auml;hrungswert 1234,5 zur&uuml;ck. Beachten Sie die Reihenfolge der beiden Alternativen: Die Suchmaschine nimmt die erste, die passt &#8211; st&uuml;nde die einfache Variante vorn, bek&auml;men wir bei <b>1.234,50<\/b> nur die <b>234,50<\/b> zu sehen. Diese Reihenfolge ist bei Alternativen fast immer entscheidend, und der Fehler ist t&uuml;ckisch, weil das Ergebnis ja plausibel aussieht.<\/p>\n<p>Bis hierhin haben wir immer nur den ersten Treffer geholt. Oft brauchen wir aber alle: s&auml;mtliche E-Mail-Adressen in einem Verteiler, alle Rechnungsnummern in einer Bemerkung, jeden Platzhalter einer Textvorlage. Daf&uuml;r lohnt sich eine vierte Hilfsfunktion namens <b>RegExAlleTreffer<\/b>, die wir zu den dreien aus dem ersten Teil stellen (siehe Listing 6). Sie folgt demselben Bauprinzip und erwartet diese Parameter:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>RegExAlleTreffer(ByVal strText<span style=\"color:blue;\"> As String<\/span>, ByVal strMuster<span style=\"color:blue;\"> As String<\/span>, _\r\n       <span style=\"color:blue;\">Optional<\/span> ByVal strTrenner<span style=\"color:blue;\"> As String<\/span> = \"; \")<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Static <\/span>objRegEx<span style=\"color:blue;\"> As Object<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>objTreffer<span style=\"color:blue;\"> As Object<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>objMatch<span style=\"color:blue;\"> As Object<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>strErgebnis<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">If <\/span>objRegEx Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n        <span style=\"color:blue;\">Set<\/span> objRegEx = CreateObject(\"VBScript.RegExp\")\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    objRegEx.Pattern = strMuster\r\n    objRegEx.Global = <span style=\"color:blue;\">True<\/span>\r\n    objRegEx.IgnoreCase = <span style=\"color:blue;\">True<\/span>\r\n    <span style=\"color:blue;\">Set<\/span> objTreffer = objRegEx.Execute(strText)\r\n    For Each objMatch In objTreffer\r\n        <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Len<\/span>(strErgebnis) &gt; 0<span style=\"color:blue;\"> Then<\/span>\r\n            strErgebnis = strErgebnis & strTrenner\r\n        <span style=\"color:blue;\">End If<\/span>\r\n        strErgebnis = strErgebnis & objMatch.Value\r\n    <span style=\"color:blue;\">Next<\/span> objMatch\r\n    RegExAlleTreffer = strErgebnis\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 6: Alle Fundstellen als Zeichenkette<\/span><\/b><\/p>\n<ul>\n<li><b>strText<\/b>: der zu durchsuchende Text.<\/li>\n<li><b>strMuster<\/b>: der regul&auml;re Ausdruck.<\/li>\n<li><b>strTrenner<\/b>: optional, Vorgabewert ist ein Semikolon mit Leerzeichen. Damit werden die gefundenen Stellen zu einer Zeichenkette verkettet.<\/li>\n<\/ul>\n<p>Die Eigenschaft <b>Global<\/b> steht hier zwingend auf <b>True<\/b> &#8211; ohne sie liefert die Suche genau einen Treffer, und die ganze Funktion w&auml;re sinnlos. Weil das Ergebnis eine einfache Zeichenkette ist, l&auml;sst sich die Funktion unmittelbar in der Feldliste einer Abfrage einsetzen.<\/p>\n<h2>Ein durchgehendes Beispiel: die Verteilerliste<\/h2>\n<p>Nehmen wir eine Aufgabe, die in der Praxis regelm&auml;&szlig;ig anf&auml;llt. Ein Mitarbeiter kopiert den Verteiler einer E-Mail in das Bemerkungsfeld eines Kundendatensatzes. Was dort ankommt, ist alles andere als einheitlich &#8211; mal mit Namen und spitzen Klammern, mal mit runden Klammern, getrennt durch Semikola, Kommata oder Zeilenumbr&uuml;che:<\/p>\n<pre><span style=\"color:blue;\">Dim <\/span>strVerteiler<span style=\"color:blue;\"> As String<\/span>\r\nstrVerteiler = _\r\n    \"Anna Meier &lt;a.meier@example.de&gt;; \" & _\r\n    \"Bernd Schulz (b.schulz@example.de), \" & <span style=\"color:blue;\">vbCrLf<\/span> & _\r\n    \"c.mueller@example.de;\" & <span style=\"color:blue;\">vbCrLf<\/span> & _\r\n    \"Dieter Wei&szlig; &lt;d.weiss@beispiel.at&gt;\"<\/pre>\n<p>F&uuml;r die Suche brauchen wir eine zweite Variante unseres E-Mail-Musters. Das Muster von weiter oben tr&auml;gt die beiden Anker und beschreibt deshalb einen Text, der ausschlie&szlig;lich aus einer Adresse besteht.<\/p>\n<p>Zum Herausl&ouml;sen aus einem l&auml;ngeren Text m&uuml;ssen die Anker weg:<\/p>\n<pre><span style=\"color:blue;\">Public <\/span>Const cstrRegExAdresse<span style=\"color:blue;\"> As String<\/span> = _\r\n    \"[A-Za-z0-9._%+\\-]+@[A-Za-z0-9.\\-]+\\.[A-Za-z]{2,}\"<\/pre>\n<p>Das ist ein Punkt, an dem viele h&auml;ngen bleiben, deshalb sei er ausdr&uuml;cklich festgehalten: Pr&uuml;fen und Extrahieren brauchen unterschiedliche Muster. Beim Pr&uuml;fen wollen wir wissen, ob der gesamte Text eine Adresse ist &#8211; beim Extrahieren suchen wir Adressen irgendwo darin. Verwenden Sie versehentlich das verankerte Muster zum Suchen, finden Sie nie etwas; verwenden Sie das ankerlose Muster zum Pr&uuml;fen, geht jeder Unsinn durch, der irgendwo ein Klammeraffe enth&auml;lt.<\/p>\n<p>Der Aufruf liefert dann eine saubere Liste:<\/p>\n<pre><span style=\"color:blue;\">Debug.Print<\/span> RegExAlleTreffer(strVerteiler, _\r\n    cstrRegExAdresse)\r\na.meier@example.de; b.schulz@example.de;\r\nc.mueller@example.de; d.weiss@beispiel.at<\/pre>\n<p>Bemerkenswert ist, was die Funktion alles ignoriert hat: die Namen davor, die spitzen und runden Klammern, die unterschiedlichen Trennzeichen und die Zeilenumbr&uuml;che. Genau das ist die St&auml;rke regul&auml;rer Ausdr&uuml;cke gegen&uuml;ber einer selbstgeschriebenen Zerlegung &#8211; wir mussten kein einziges dieser Sonderzeichen vorhersehen.<\/p>\n<h2>Von der Zeichenkette zum Array<\/h2>\n<p>F&uuml;r die Anzeige ist eine verkettete Zeichenkette ideal, f&uuml;r die Weiterverarbeitung nicht. Wenn wir jede Adresse einzeln in eine Tabelle schreiben oder als Empf&auml;nger an eine E-Mail h&auml;ngen wollen, brauchen wir ein Array. Die Variante aus Listing 7 liefert genau das.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>RegExTrefferArray(ByVal strText<span style=\"color:blue;\"> As String<\/span>, ByVal strMuster<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Variant<\/span>\r\n    <span style=\"color:blue;\">Static <\/span>objRegEx<span style=\"color:blue;\"> As Object<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>objTreffer<span style=\"color:blue;\"> As Object<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>strErgebnis()<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>i<span style=\"color:blue;\"> As Long<\/span>\r\n    <span style=\"color:blue;\">If <\/span>objRegEx Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n        <span style=\"color:blue;\">Set<\/span> objRegEx = CreateObject(\"VBScript.RegExp\")\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    objRegEx.Pattern = strMuster\r\n    objRegEx.Global = <span style=\"color:blue;\">True<\/span>\r\n    objRegEx.IgnoreCase = <span style=\"color:blue;\">True<\/span>\r\n    <span style=\"color:blue;\">Set<\/span> objTreffer = objRegEx.Execute(strText)\r\n    ' Kein Treffer: leeres Array zurueckgeben\r\n    <span style=\"color:blue;\">If <\/span>objTreffer.Count = 0<span style=\"color:blue;\"> Then<\/span>\r\n        RegExTrefferArray = Array()\r\n        <span style=\"color:blue;\">Exit Function<\/span>\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    ReDim strErgebnis(objTreffer.Count - 1)\r\n    For i = 0 To objTreffer.Count - 1\r\n        strErgebnis(i) = objTreffer(i).Value\r\n    <span style=\"color:blue;\">Next<\/span> i\r\n    RegExTrefferArray = strErgebnis\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 7: Die Trefferliste als Array<\/span><\/b><\/p>\n<p>Der R&uuml;ckgabewert im Fall ohne Treffer verdient Beachtung. Wir liefern mit <b>Array()<\/b> ein leeres Array zur&uuml;ck, dessen obere Grenze bei -1 liegt. Eine anschlie&szlig;ende Schleife von 0 bis <b>UBound<\/b> l&auml;uft damit schlicht null Mal durch &#8211; ohne dass der Aufrufer den Sonderfall abfangen m&uuml;sste. H&auml;tten wir stattdessen <b>Null<\/b> oder ein nicht dimensioniertes Array zur&uuml;ckgegeben, br&auml;uchte jeder Aufruf eine zus&auml;tzliche Pr&uuml;fung.<\/p>\n<h2>Die Adressen in eine Tabelle &uuml;bernehmen<\/h2>\n<p>Nun setzen wir alles zusammen. Die folgende Funktion durchl&auml;uft alle Kunden mit einem gef&uuml;llten Bemerkungsfeld, holt daraus s&auml;mtliche Adressen und schreibt sie in eine Tabelle <b>tblEMailAdressen<\/b> mit den Feldern <b>KundeID<\/b> und <b>EMail<\/b>. Dubletten &uuml;berspringt sie (siehe Listing 8).<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>AdressenUebernehmen()<span style=\"color:blue;\"> As Long<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>db<span style=\"color:blue;\"> As <\/span>DAO.Database\r\n    <span style=\"color:blue;\">Dim <\/span>rstQuelle<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n    <span style=\"color:blue;\">Dim <\/span>rstZiel<span style=\"color:blue;\"> As <\/span>DAO.Recordset\r\n    <span style=\"color:blue;\">Dim <\/span>objBekannt<span style=\"color:blue;\"> As Object<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>varTreffer<span style=\"color:blue;\"> As Variant<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>strAdresse<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>lngNeu<span style=\"color:blue;\"> As Long<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>i<span style=\"color:blue;\"> As Long<\/span>\r\n    <span style=\"color:blue;\">Set<\/span> db = CurrentDb\r\n    <span style=\"color:blue;\">Set<\/span> objBekannt = CreateObject(\"Scripting.Dictionary\")\r\n    objBekannt.CompareMode = 1\r\n    <span style=\"color:blue;\">Set<\/span> rstQuelle = db.OpenRecordset(\"SELECT KundeID, Notiz FROM tblKunden \" & \"WHERE Notiz Is <span style=\"color:blue;\">Not<\/span> Null\")\r\n    <span style=\"color:blue;\">Set<\/span> rstZiel = db.OpenRecordset(\"tblEMailAdressen\", dbOpenDynaset, dbAppendOnly)\r\n    <span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rstQuelle.EOF\r\n        varTreffer = RegExTrefferArray(rstQuelle!Notiz, cstrRegExAdresse)\r\n        For i = 0 To <span style=\"color:blue;\">UBound<\/span>(varTreffer)\r\n            strAdresse = LCase(varTreffer(i))\r\n            <span style=\"color:blue;\">If <\/span><span style=\"color:blue;\">Not<\/span> objBekannt.Exists(strAdresse)<span style=\"color:blue;\"> Then<\/span>\r\n                objBekannt.Add strAdresse, <span style=\"color:blue;\">True<\/span>\r\n                rstZiel.Add<span style=\"color:blue;\">New<\/span>\r\n                rstZiel!KundeID = rstQuelle!KundeID\r\n                rstZiel!EMail = strAdresse\r\n                rstZiel.Update\r\n                lngNeu = lngNeu + 1\r\n            <span style=\"color:blue;\">End If<\/span>\r\n        <span style=\"color:blue;\">Next<\/span> i\r\n        rstQuelle.Move<span style=\"color:blue;\">Next<\/span>\r\n    <span style=\"color:blue;\">Loop<\/span>\r\n    rstZiel.Close\r\n    rstQuelle.Close\r\n    <span style=\"color:blue;\">Set<\/span> rstZiel = Nothing\r\n    <span style=\"color:blue;\">Set<\/span> rstQuelle = Nothing\r\n    AdressenUebernehmen = lngNeu\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 8: Adressen aus Freitext in eine eigene Tabelle &uuml;berf&uuml;hren<\/span><\/b><\/p>\n<p>F&uuml;r drei Details lohnt sich ein zweiter Blick. Die Zuweisung <b>CompareMode = 1<\/b> stellt das Dictionary auf einen Vergleich um, der Gro&szlig;- und Kleinschreibung ignoriert &#8211; zusammen mit dem <b>LCase<\/b> beim Einlesen erwischen wir damit auch Adressen, die in unterschiedlicher Schreibweise vorliegen. Beachten Sie au&szlig;erdem, dass <b>CompareMode<\/b> gesetzt sein muss, <b>bevor<\/b> der erste Eintrag hinzugef&uuml;gt wird.<\/p>\n<p>Zweitens wirkt das Dictionary &uuml;ber den gesamten Durchlauf. Eine Adresse, die bei zwei Kunden auftaucht, landet also nur einmal in der Zieltabelle &#8211; beim erstgefundenen Kunden. Soll die Dublettenpr&uuml;fung stattdessen nur innerhalb eines Kunden greifen, rufen Sie zu Beginn jedes Schleifendurchlaufs <b>objBekannt.RemoveAll<\/b> auf.<\/p>\n<p>Und drittens ein beruhigender Hinweis zum <b>Scripting.Dictionary<\/b>: Es stammt aus der Bibliothek <b>scrrun.dll<\/b> und ist von der im ersten Teil beschriebenen Abk&uuml;ndigung von VBScript nicht betroffen. Sie d&uuml;rfen es also bedenkenlos weiterverwenden.<\/p>\n<h2>Nur z&auml;hlen statt sammeln<\/h2>\n<p>Manchmal interessiert uns gar nicht, was gefunden wurde, sondern nur, wie oft. Wie viele Adressen stecken in einer Bemerkung? Wie viele Platzhalter enth&auml;lt eine Vorlage? Daf&uuml;r gen&uuml;gt ein Einzeiler, der auf dem Array aufsetzt:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>RegExAnzahl( _\r\n        ByVal strText<span style=\"color:blue;\"> As String<\/span>, _\r\n        ByVal strMuster<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As Long<\/span>\r\n    RegExAnzahl = <span style=\"color:blue;\">UBound<\/span>( _\r\n        RegExTrefferArray(strText, strMuster)) + 1\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Weil das leere Array die obere Grenze <b>-1<\/b> hat, liefert die Funktion im Fall ohne Treffer sauber den Wert <b>0 <\/b>zur&uuml;ck &#8211; ganz ohne Sonderbehandlung.<\/p>\n<p>Damit l&auml;sst sich nun endlich die Frage beantworten, bei welchen Kunden &uuml;berhaupt mehr als eine Adresse im Bemerkungsfeld steckt. Hier lauert allerdings eine Falle: In Abfragen k&ouml;nnen Sie keine VBA-Konstanten verwenden.<\/p>\n<p>Access l&ouml;st zwar Funktionsaufrufe auf, Konstanten aber nicht, und quittiert den Versuch mit der Meldung &uuml;ber eine undefinierte Funktion. Die L&ouml;sung ist eine winzige Funktion, die das Muster zur&uuml;ckliefert:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>MusterAdresse()<span style=\"color:blue;\"> As String<\/span>\r\n    MusterAdresse = cstrRegExAdresse\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Und damit funktioniert die Auswertung wie erwartet:<\/p>\n<pre>SELECT KundeID, Notiz,\r\n    RegExAnzahl(Notiz, MusterAdresse()) AS Adressen,\r\n    RegExAlleTreffer(Notiz, MusterAdresse()) AS Liste\r\nFROM tblKunden\r\nWHERE RegExAnzahl(Notiz, MusterAdresse()) &gt; 1<\/pre>\n<p>Diese Abfrage ist &uuml;brigens ein hervorragender Ausgangspunkt, bevor Sie die &Uuml;bernahme mit der Prozedur <b>AdressenUebernehmen<\/b> tats&auml;chlich starten. Sie zeigt Ihnen auf einen Blick, was die Funktion finden wird &#8211; und ob Ihr Muster vielleicht Dinge einsammelt, die gar keine Adressen sind.<\/p>\n<h2>Rezept 4: Texte bereinigen<\/h2>\n<p>Wenn Benutzer Texte aus Webseiten oder Word-Dokumenten in ein Memofeld kopieren, kommt allerlei mit: HTML-Auszeichnungen, gesch&uuml;tzte Leerzeichen, doppelte Leerzeichen und Zeilenumbr&uuml;che. Drei kleine Funktionen r&auml;umen das auf.<\/p>\n<p>Die erste ist ein Einzeiler und zugleich die n&uuml;tzlichste. Sie fasst jede Folge von Leerzeichen, Tabulatoren und Zeilenumbr&uuml;chen zu einem einzigen Leerzeichen zusammen und entfernt anschlie&szlig;end den Leerraum an den Enden:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>LeerraumNormalisieren( _\r\n        ByVal strText<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n    LeerraumNormalisieren = <span style=\"color:blue;\">Trim<\/span>( _\r\n        RegExErsetzen(strText, \"\\s+\", \" \"))\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Das Muster <b>\\s+<\/b> trifft dabei alle Arten von Leerraum. Wollen Sie die Abs&auml;tze erhalten und nur innerhalb der Zeilen aufr&auml;umen, verwenden Sie stattdessen <b>[ \\t]+<\/b> &#8211; dann bleiben die Umbr&uuml;che unangetastet.<\/p>\n<p>Darauf setzt die zweite Funktion auf. Sie entfernt zun&auml;chst die Auszeichnungen, ersetzt danach die h&auml;ufigsten kodierten Zeichen und &uuml;bergibt das Ergebnis zum Schluss an die Funktion <b>LeerraumNormalisieren<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>HtmlEntfernen( _\r\n        ByVal strText<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>strErgebnis<span style=\"color:blue;\"> As String<\/span>\r\n    ' Auszeichnungen entfernen\r\n    strErgebnis = RegExErsetzen(strText, \"&lt;[^&gt;]+&gt;\", \" \")\r\n    ' Haeufige kodierte Zeichen aufloesen\r\n    strErgebnis = <span style=\"color:blue;\">Replace<\/span>(strErgebnis, \" \", \" \")\r\n    strErgebnis = <span style=\"color:blue;\">Replace<\/span>(strErgebnis, \"&quot;\", Chr(34))\r\n    strErgebnis = <span style=\"color:blue;\">Replace<\/span>(strErgebnis, \"&lt;\", \"&lt;\")\r\n    strErgebnis = <span style=\"color:blue;\">Replace<\/span>(strErgebnis, \"&gt;\", \"&gt;\")\r\n    strErgebnis = <span style=\"color:blue;\">Replace<\/span>(strErgebnis, \"&amp;\", \"&\")\r\n    ' Leerraum vereinheitlichen\r\n    HtmlEntfernen = LeerraumNormalisieren(strErgebnis)\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Drei Punkte verdienen dabei Beachtung. Erstens ersetzen wir die Auszeichnungen durch ein Leerzeichen und nicht durch einen Leerstring. Andernfalls klebten zwei W&ouml;rter zusammen, die im Original durch einen Absatzwechsel getrennt waren &#8211; den &uuml;berz&auml;hligen Leerraum r&auml;umt der letzte Schritt ohnehin wieder weg.<\/p>\n<p>Zweitens steht <b>&amp;<\/b> bewusst am Ende der Ersetzungen. Im Original kann durchaus <b>&amp;lt;<\/b> stehen, wenn dort ein Kleinerzeichen als Text gemeint war. Ersetzten wir das kaufm&auml;nnische <b>Und<\/b> zuerst, entst&uuml;nde daraus <b>&lt;<\/b>, und der n&auml;chste Schritt machte daraus f&auml;lschlich eine spitze Klammer.<\/p>\n<p>Drittens deckt die Liste nur die h&auml;ufigsten F&auml;lle ab. Begegnen Ihnen in Ihren Daten weitere kodierte Zeichen &#8211; etwa <b>&euro;<\/b> f&uuml;r das Eurozeichen oder <b>&uuml;<\/b> f&uuml;r ein u mit Umlautpunkten &#8211; erg&auml;nzen Sie einfach eine weitere Zeile nach demselben Muster, aber oberhalb der Zeile f&uuml;r <b>&amp;<\/b>. Beachten Sie au&szlig;erdem, dass die Funktion nur die Auszeichnungen entfernt, nicht deren Inhalte: Bei einem Skriptblock bliebe der Programmcode stehen.<\/p>\n<p>Die dritte Funktion geh&ouml;rt streng genommen in eine andere Ecke, arbeitet aber nach demselben Prinzip. Sie s&auml;ubert einen Wert aus der Datenbank, bevor wir ihn als Dateinamen f&uuml;r einen Export verwenden:<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>DateinameSaeubern( _\r\n        ByVal strName<span style=\"color:blue;\"> As String<\/span>)<span style=\"color:blue;\"> As String<\/span>\r\n    DateinameSaeubern = RegExErsetzen(strName, _\r\n        \"[<\/font>\/:*?\"\"&lt;&gt;|]\", \"_\")\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p>Die doppelten Anf&uuml;hrungszeichen innerhalb der Zeichenkette sind die &uuml;bliche VBA-Schreibweise f&uuml;r ein einzelnes Anf&uuml;hrungszeichen &#8211; im Muster steht also tats&auml;chlich nur ein Zeichen. Der Backslash am Anfang der Klasse ist verdoppelt, weil er in einem regul&auml;ren Ausdruck sonst das folgende Zeichen maskieren w&uuml;rde.<\/p>\n<p>Ein kurzer Test in einer Testprozedur zeigt alle drei Funktionen im Einsatz:<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>Test_Rezept4()\r\n    <span style=\"color:blue;\">Dim <\/span>strRoh<span style=\"color:blue;\"> As String<\/span>\r\n    \r\n    strRoh = \"&lt;p&gt;Lieferung &lt;b&gt;frei Haus&lt;\/b&gt;&lt;\/p&gt;\" _\r\n        & \"&lt;p&gt;ab 250,00 Euro &amp; portofrei.&lt;\/p&gt;\"\r\n    \r\n    <span style=\"color:blue;\">Debug.Print<\/span> HtmlEntfernen(strRoh)\r\n    <span style=\"color:blue;\">Debug.Print<\/span> LeerraumNormalisieren(\"  Zu   viel  Luft \")\r\n    <span style=\"color:blue;\">Debug.Print<\/span> DateinameSaeubern(\"Preis 12\/2026 &lt;netto&gt;\")\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p>Hier ist das Ergebnis:<\/p>\n<pre>Lieferung frei Haus ab 250,00 Euro & portofrei.\r\nZu viel Luft\r\nPreis 12_2026 _netto_<\/pre>\n<p>Am ersten Ergebnis l&auml;sst sich der eingangs erw&auml;hnte Punkt gut nachvollziehen: Zwischen <b>Haus<\/b> und <b>ab<\/b> stand im Original &uuml;berhaupt kein Leerzeichen, sondern nur die Folge aus schlie&szlig;endem und &ouml;ffnendem Absatz. Weil wir jede Auszeichnung durch ein Leerzeichen ersetzen, stehen die beiden W&ouml;rter am Ende trotzdem sauber getrennt da.<\/p>\n<h2>Rezept 5: Textvorlagen f&uuml;llen<\/h2>\n<p>Das n&uuml;tzlichste Rezept zum Schluss. Wer Serienbriefe, E-Mail-Vorlagen oder Best&auml;tigungstexte aus der Datenbank heraus erzeugt, arbeitet gegebenenfalls mit Platzhaltern in geschweiften Klammern. Die Funktion aus Listing 9 durchsucht die Vorlage nach solchen Platzhaltern und ersetzt sie durch die gleichnamigen Felder eines Recordsets.<\/p>\n<pre><span style=\"color:blue;\">Public Function <\/span>VorlageFuellen(ByVal strVorlage<span style=\"color:blue;\"> As String<\/span>, ByRef rst<span style=\"color:blue;\"> As <\/span>DAO.Recordset)<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Static <\/span>objRegEx<span style=\"color:blue;\"> As Object<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>objTreffer<span style=\"color:blue;\"> As Object<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>objMatch<span style=\"color:blue;\"> As Object<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>strErgebnis<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>strFeld<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">Dim <\/span>strWert<span style=\"color:blue;\"> As String<\/span>\r\n    <span style=\"color:blue;\">If <\/span>objRegEx Is Nothing<span style=\"color:blue;\"> Then<\/span>\r\n        <span style=\"color:blue;\">Set<\/span> objRegEx = CreateObject(\"VBScript.RegExp\")\r\n    <span style=\"color:blue;\">End If<\/span>\r\n    objRegEx.Pattern = \"\\{([A-Za-z0-9_]+)\\}\"\r\n    objRegEx.Global = <span style=\"color:blue;\">True<\/span>\r\n    strErgebnis = strVorlage\r\n    <span style=\"color:blue;\">Set<\/span> objTreffer = objRegEx.Execute(strVorlage)\r\n    For Each objMatch In objTreffer\r\n        strFeld = objMatch.SubMatches(0)\r\n        strWert = \"\"\r\n        On Error Resume <span style=\"color:blue;\">Next<\/span>\r\n        strWert = Nz(rst.Fields(strFeld).Value, \"\")\r\n        <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n        strErgebnis = <span style=\"color:blue;\">Replace<\/span>(strErgebnis, objMatch.Value, strWert)\r\n    <span style=\"color:blue;\">Next<\/span> objMatch\r\n    VorlageFuellen = strErgebnis\r\n<span style=\"color:blue;\">End Function<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 9: Ersetzen von Platzhaltern durch Feldinhalte<\/span><\/b><\/p>\n<p>Mit einer Vorlage wie <b>Sehr geehrte(r) {Anrede} {Nachname}, Ihre Bestellung {BestellNr} ist unterwegs.<\/b> und einem passenden Recordset entsteht daraus der fertige Text.<\/p>\n<p>Nicht vorhandene Felder werden dank der Fehlerbehandlung durch einen Leerstring ersetzt, statt die ganze Funktion abst&uuml;rzen zu lassen.<\/p>\n<p>Wollen Sie die verwendeten Platzhalter vorab kennen &#8211; etwa um dem Benutzer anzuzeigen, welche Felder eine Vorlage ben&ouml;tigt &#8211; gen&uuml;gt ein Aufruf der Funktion <b>RegExAlleTreffer<\/b>:<\/p>\n<pre><span style=\"color:blue;\">Debug.Print<\/span> RegExAlleTreffer(strVorlage, _\r\n    \"\\{[A-Za-z0-9_]+\\}\")\r\n{Anrede}; {Nachname}; {BestellNr}<\/pre>\n<h2>Die Vorlagen in einer Tabelle<\/h2>\n<p>In der Praxis stehen die Vorlagen nat&uuml;rlich nicht im Code, sondern in einer Tabelle &#8211; nur so k&ouml;nnen Anwender sie selbst pflegen. In der Beispieldatenbank hei&szlig;t diese Tabelle <b>tblVorlagen<\/b> und kommt mit drei Feldern aus: einer <b>Bezeichnung<\/b>, &uuml;ber die wir die Vorlage im Code ansprechen, einem <b>Betreff<\/b> und dem eigentlichen <b>Vorlagentext<\/b>. Bild 1 zeigt die Tabelle mit den drei mitgelieferten Vorlagen.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2026_05\/pic_1617_001.png\" alt=\"Die Tabelle tblVorlagen mit den Beispielvorlagen\" width=\"699,559\" height=\"372,3047\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Die Tabelle tblVorlagen mit den Beispielvorlagen<\/span><\/b><\/p>\n<p>Auf dem Feld <b>Bezeichnung<\/b> liegt ein eindeutiger Index, damit der Zugriff &uuml;ber den Namen verl&auml;sslich genau eine Vorlage liefert.<\/p>\n<p>Die drei Beispielvorlagen sind bewusst unterschiedlich angelegt. <b>Versandbestaetigung<\/b> und <b>Datencheck<\/b> verwenden ausschlie&szlig;lich Platzhalter, zu denen es in der Kundentabelle auch tats&auml;chlich Felder gibt. Die Vorlage <b>Anfrage<\/b> enth&auml;lt dagegen zus&auml;tzlich den Platzhalter <b>{Ansprechpartner}<\/b> &#8211; ein Feld, das gar nicht existiert. Damit l&auml;sst sich unmittelbar nachvollziehen, dass die Fehlerbehandlung in der Funktion <b>VorlageFuellen<\/b> tut, was sie soll: Statt eines Laufzeitfehlers bleibt an dieser Stelle schlicht nichts stehen.<\/p>\n<h2>Der Aufruf mit einem Recordset<\/h2>\n<p>Den eigentlichen Aufruf zeigen zwei Prozeduren in der Beispieldatenbank, die Sie im Modul <b>mdlVorlagenAufruf<\/b> finden. Die erste, <b>VorlageFuellenBeispiel<\/b>, arbeitet in drei Schritten: Sie holt die Vorlage &uuml;ber ihre Bezeichnung aus <b>tblVorlagen<\/b>, &ouml;ffnet ein Recordset mit dem gew&uuml;nschten Kundendatensatz und &uuml;bergibt beides an <b>VorlageFuellen<\/b>. Das Ergebnis landet im Direktbereich.<\/p>\n<p>Interessanter ist die zweite Prozedur <b>SerienbriefeErzeugen<\/b>, weil sie den entscheidenden Punkt vorf&uuml;hrt: Die Vorlage wird <b>einmal<\/b> geladen, anschlie&szlig;end wandert das Recordset mit <b>MoveNext<\/b> von Datensatz zu Datensatz. &Uuml;bergeben wird dabei jedes Mal dasselbe Objekt &#8211; und trotzdem entsteht jedes Mal ein anderer Text, weil <b>VorlageFuellen<\/b> immer den gerade aktuellen Datensatz ausliest. Genau deshalb erwartet die Funktion ein Recordset und keine Datensatznummer.<\/p>\n<p>Nebenbei f&uuml;hrt die Prozedur noch vor, dass sich auch der <b>Betreff<\/b> durch dieselbe Funktion schicken l&auml;sst. In den mitgelieferten Vorlagen stehen dort zwar keine Platzhalter, aber ein Betreff wie <b>Ihre Bestellung bei {Firma}<\/b> w&uuml;rde ohne jede weitere &Auml;nderung funktionieren.<\/p>\n<h2>Vorlagen in einer Abfrage einsetzen<\/h2>\n<p>Naheliegend w&auml;re nun, die gef&uuml;llten Texte gleich in einer Abfrage zu erzeugen. Das scheitert aber daran, dass <b>VorlageFuellen<\/b> ein Recordset erwartet &#8211; und ein solches l&auml;sst sich in einer Abfrage nicht &uuml;bergeben. Die L&ouml;sung ist eine kleine Zwischenfunktion namens <b>VorlageText<\/b>, die Sie ebenfalls in der Beispieldatenbank finden. Sie nimmt lediglich die Bezeichnung der Vorlage und eine Kundennummer entgegen und besorgt sich beide Recordsets selbst.<\/p>\n<p>Damit funktioniert dann diese Abfrage:<\/p>\n<pre>SELECT KundeID, Firma, EMail,\r\n    VorlageText(\"Versandbestaetigung\", [KundeID])\r\n        AS Nachricht\r\nFROM tblKunden\r\nWHERE EMail Is <span style=\"color:blue;\">Not<\/span> Null\r\nORDER BY Firma<\/pre>\n<p>Ein Wort zur Geschwindigkeit: Eine solche Abfrage ruft die Funktion f&uuml;r jeden einzelnen Datensatz auf. <b>VorlageText<\/b> h&auml;lt die zuletzt geladene Vorlage deshalb in <b>Static<\/b>-Variablen vor, sodass <b>tblVorlagen<\/b> nur einmal statt tausendfach gelesen wird. Das Recordset auf den Kundendatensatz muss dagegen jedes Mal ge&ouml;ffnet werden.<\/p>\n<p>F&uuml;r eine Schleife &uuml;ber viele Datens&auml;tze ist die Variante aus <b>SerienbriefeErzeugen<\/b> daher deutlich sparsamer &#8211; die Abfrage spielt ihre St&auml;rke dort aus, wo Sie das Ergebnis unmittelbar als Datenherkunft eines Formulars oder Berichts brauchen.<\/p>\n<h2>Einsatz in Abfragen<\/h2>\n<p>Alle vorgestellten Funktionen lassen sich unmittelbar in Abfragen verwenden, was ihnen ihren eigentlichen Wert gibt. Eine Abfrage, die alle Datens&auml;tze mit fragw&uuml;rdigen E-Mail-Adressen auflistet, sieht so aus:<\/p>\n<pre>SELECT KundeID, Firma, EMail\r\nFROM tblKunden\r\nWHERE EMail Is <span style=\"color:blue;\">Not<\/span> Null\r\n    AND RegExTest(EMail, cstrRegExEMail) = <span style=\"color:blue;\">False<\/span><\/pre>\n<p>Und die dazu passende Aktualisierungsabfrage, die den kompletten Telefonbestand vereinheitlicht:<\/p>\n<pre>UPDATE tblKunden\r\nSET Telefon = TelefonNormalisieren(Telefon)\r\nWHERE Telefon Is <span style=\"color:blue;\">Not<\/span> Null<\/pre>\n<p>Zwei Dinge sollten Sie dabei beachten. Zum einen laufen die Funktionen bei jedem einzelnen Datensatz durch die VBA-Engine, was bei mehreren hunderttausend Datens&auml;tzen sp&uuml;rbar wird &#8211; genau daf&uuml;r haben wir die RegExp-Objekte in <b>Static<\/b>-Variablen abgelegt. Zum anderen sind solche Aktualisierungen nicht umkehrbar. Legen Sie vorher eine Sicherungskopie an oder schreiben Sie die alten Werte in ein zus&auml;tzliches Feld.<\/p>\n<h2>Die Falle, die Access zum Stillstand bringt<\/h2>\n<p>Zum Abschluss ein Ph&auml;nomen, das Sie kennen sollten, bevor es Ihnen begegnet. Bestimmte Muster k&ouml;nnen die Suchmaschine in eine Sackgasse f&uuml;hren, aus der sie erst nach vielen Versuchen wieder herausfindet. Der Fachbegriff daf&uuml;r lautet katastrophales Backtracking, und das Ergebnis ist eine Access-Instanz, die sich nur noch &uuml;ber den Task-Manager beenden l&auml;sst.<\/p>\n<p>Ein typischer Ausl&ouml;ser ist ein Quantor innerhalb einer Gruppe, die selbst wieder einen Quantor tr&auml;gt. Auch sich &uuml;berschneidende Alternativen k&ouml;nnen das Problem verursachen. Ein harmlos aussehendes Beispiel:<\/p>\n<pre>' NICHT ausprobieren!\r\n<span style=\"color:blue;\">Debug.Print<\/span> RegExTest( _\r\n    \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!\", \"^(a+)+$\")<\/pre>\n<p>Weil der Text nicht passt &#8211; am Ende steht ein Ausrufezeichen statt eines <b>a<\/b> -, probiert die Suchmaschine immer neue M&ouml;glichkeiten durch, die drei&szlig;ig <b>a<\/b> auf die beiden Quantoren zu verteilen. Allein die Zahl der m&ouml;glichen Aufteilungen liegt bei mehr als einer halben Milliarde, und mit jedem zus&auml;tzlichen Zeichen verdoppelt sie sich. Ein Zeitlimit gibt es nicht.<\/p>\n<p>Drei Regeln halten Sie davon fern:<\/p>\n<ul>\n<li>Vermeiden Sie verschachtelte Quantoren, wenn sich ihre Trefferbereiche &uuml;berschneiden. Statt <b>(a+)+<\/b> schreiben Sie schlicht <b>a+<\/b>.<\/li>\n<li>Ziehen Sie Zeichenklassen dem Punkt vor. <b>[^>]+<\/b> ist nicht nur schneller als <b>.+?<\/b>, sondern auch berechenbarer.<\/li>\n<li>Setzen Sie beide Anker, wenn Sie den gesamten Text pr&uuml;fen. Damit kann die Suchmaschine fr&uuml;h abbrechen, statt an jeder Position neu anzusetzen.<\/li>\n<\/ul>\n<p>Testen Sie neue Muster au&szlig;erdem grunds&auml;tzlich zuerst im Direktbereich und nicht gleich in einer Abfrage &uuml;ber 10.000 Datens&auml;tze. Ein Muster, das bei einem Aufruf zwei Sekunden braucht, macht die Abfrage unbenutzbar &#8211; und die Ursache ist dann deutlich schwerer zu finden.<\/p>\n<h2>Zusammenfassung<\/h2>\n<p>Mit den vier Hilfsfunktionen aus beiden Teilen dieser Reihe &#8211; pr&uuml;fen, ersetzen, eine Gruppe auslesen und alle Treffer sammeln &#8211; decken Sie den weitaus gr&ouml;&szlig;ten Teil dessen ab, was im Alltag anf&auml;llt. Die eigentliche Arbeit liegt im Formulieren der Muster, und dabei gilt: Ein pragmatisches Muster, das die h&auml;ufigen Fehler abf&auml;ngt, ist einem perfekten vorzuziehen, das niemand mehr lesen kann.<\/p>\n<p>Denken Sie zum Schluss noch einmal an die Empfehlung aus dem ersten Teil: Binden Sie das <b>RegExp<\/b>-Objekt in ausgelieferten Anwendungen &uuml;ber <b>CreateObject<\/b> ein. Nur diese Variante &uuml;berlebt sowohl &auml;ltere Systeme mit aktivem VBScript als auch die k&uuml;nftigen Windows-Versionen, in denen VBScript nicht mehr vorhanden ist.<\/p>\n<h2>Downloads zu diesem Beitrag<\/h2>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>RegulaereAusdruecke_Teil2.accdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/07C458C0-F077-43FA-A284-CA3D6B03168B\/aiu_1617.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Im ersten Teil dieser Reihe haben wir das RegExp-Objekt eingebunden, seine vier Klassen kennengelernt und die Mustersprache durchgearbeitet. Nun wird es konkret. Dieser Beitrag liefert fertige Muster f&uuml;r die Pr&uuml;fung von E-Mail-Adressen, Telefonnummern, Postleitzahlen und Kfz-Kennzeichen, dazu Funktionen zum Herausl&ouml;sen von Daten aus Freitextfeldern, zum Bereinigen kopierter Texte und zum F&uuml;llen von Textvorlagen. Alle Beispiele setzen die drei Hilfsfunktionen aus dem ersten Teil voraus &#8211; und am Ende zeigen wir eine Falle, die eine Access-Anwendung zuverl&auml;ssig zum Stillstand bringt.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[662026,66052026,44000025],"tags":[],"class_list":["post-55001617","post","type-post","status-publish","format-standard","hentry","category-662026","category-66052026","category-VBA_und_Programmiertechniken"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.9 (Yoast SEO v28.6) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Regul&auml;re Ausdr&uuml;cke in VBA, Teil 2: Praxis-Rezepte - Access im Unternehmen<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Regul&auml;re Ausdr&uuml;cke in VBA, Teil 2: Praxis-Rezepte\" \/>\n<meta property=\"og:description\" content=\"Im ersten Teil dieser Reihe haben wir das RegExp-Objekt eingebunden, seine vier Klassen kennengelernt und die Mustersprache durchgearbeitet. Nun wird es konkret. Dieser Beitrag liefert fertige Muster f&uuml;r die Pr&uuml;fung von E-Mail-Adressen, Telefonnummern, Postleitzahlen und Kfz-Kennzeichen, dazu Funktionen zum Herausl&ouml;sen von Daten aus Freitextfeldern, zum Bereinigen kopierter Texte und zum F&uuml;llen von Textvorlagen. Alle Beispiele setzen die drei Hilfsfunktionen aus dem ersten Teil voraus - und am Ende zeigen wir eine Falle, die eine Access-Anwendung zuverl&auml;ssig zum Stillstand bringt.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2026-10-05T19:31:52+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg01.met.vgwort.de\/na\/9b2f4c578aaf4742884daa81fe9c552f\" \/>\n<meta name=\"author\" content=\"Andr\u00e9 Minhorst\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Andr\u00e9 Minhorst\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"23\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Regul&auml;re Ausdr&uuml;cke in VBA, Teil 2: Praxis-Rezepte\",\"datePublished\":\"2026-10-05T19:31:52+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\\\/\"},\"wordCount\":3550,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg01.met.vgwort.de\\\/na\\\/9b2f4c578aaf4742884daa81fe9c552f\",\"articleSection\":[\"2026\",\"5\\\/2026\",\"VBA und Programmiertechniken\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\\\/\",\"name\":\"Regul&auml;re Ausdr&uuml;cke in VBA, Teil 2: Praxis-Rezepte - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg01.met.vgwort.de\\\/na\\\/9b2f4c578aaf4742884daa81fe9c552f\",\"datePublished\":\"2026-10-05T19:31:52+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg01.met.vgwort.de\\\/na\\\/9b2f4c578aaf4742884daa81fe9c552f\",\"contentUrl\":\"http:\\\/\\\/vg01.met.vgwort.de\\\/na\\\/9b2f4c578aaf4742884daa81fe9c552f\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Regul&auml;re Ausdr&uuml;cke in VBA, Teil 2: Praxis-Rezepte\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"name\":\"Access im Unternehmen\",\"description\":\"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access\",\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/access-im-unternehmen.de\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\",\"name\":\"Andr\u00e9 Minhorst Verlag\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"contentUrl\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"width\":370,\"height\":111,\"caption\":\"Andr\u00e9 Minhorst Verlag\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\",\"name\":\"Andr\u00e9 Minhorst\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"caption\":\"Andr\u00e9 Minhorst\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Regul&auml;re Ausdr&uuml;cke in VBA, Teil 2: Praxis-Rezepte - Access im Unternehmen","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/","og_locale":"de_DE","og_type":"article","og_title":"Regul&auml;re Ausdr&uuml;cke in VBA, Teil 2: Praxis-Rezepte","og_description":"Im ersten Teil dieser Reihe haben wir das RegExp-Objekt eingebunden, seine vier Klassen kennengelernt und die Mustersprache durchgearbeitet. Nun wird es konkret. Dieser Beitrag liefert fertige Muster f&uuml;r die Pr&uuml;fung von E-Mail-Adressen, Telefonnummern, Postleitzahlen und Kfz-Kennzeichen, dazu Funktionen zum Herausl&ouml;sen von Daten aus Freitextfeldern, zum Bereinigen kopierter Texte und zum F&uuml;llen von Textvorlagen. Alle Beispiele setzen die drei Hilfsfunktionen aus dem ersten Teil voraus - und am Ende zeigen wir eine Falle, die eine Access-Anwendung zuverl&auml;ssig zum Stillstand bringt.","og_url":"https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/","og_site_name":"Access im Unternehmen","article_published_time":"2026-10-05T19:31:52+00:00","og_image":[{"url":"http:\/\/vg01.met.vgwort.de\/na\/9b2f4c578aaf4742884daa81fe9c552f","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"23\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Regul&auml;re Ausdr&uuml;cke in VBA, Teil 2: Praxis-Rezepte","datePublished":"2026-10-05T19:31:52+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/"},"wordCount":3550,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/#primaryimage"},"thumbnailUrl":"http:\/\/vg01.met.vgwort.de\/na\/9b2f4c578aaf4742884daa81fe9c552f","articleSection":["2026","5\/2026","VBA und Programmiertechniken"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/","url":"https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/","name":"Regul&auml;re Ausdr&uuml;cke in VBA, Teil 2: Praxis-Rezepte - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/#primaryimage"},"thumbnailUrl":"http:\/\/vg01.met.vgwort.de\/na\/9b2f4c578aaf4742884daa81fe9c552f","datePublished":"2026-10-05T19:31:52+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/#primaryimage","url":"http:\/\/vg01.met.vgwort.de\/na\/9b2f4c578aaf4742884daa81fe9c552f","contentUrl":"http:\/\/vg01.met.vgwort.de\/na\/9b2f4c578aaf4742884daa81fe9c552f"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Regulaere_Ausdruecke_in_VBA_Teil_2_PraxisRezepte\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Regul&auml;re Ausdr&uuml;cke in VBA, Teil 2: Praxis-Rezepte"}]},{"@type":"WebSite","@id":"https:\/\/access-im-unternehmen.de\/#website","url":"https:\/\/access-im-unternehmen.de\/","name":"Access im Unternehmen","description":"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access","publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/access-im-unternehmen.de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/access-im-unternehmen.de\/#organization","name":"Andr\u00e9 Minhorst Verlag","url":"https:\/\/access-im-unternehmen.de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/","url":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","contentUrl":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","width":370,"height":111,"caption":"Andr\u00e9 Minhorst Verlag"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f","name":"Andr\u00e9 Minhorst","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","caption":"Andr\u00e9 Minhorst"}}]}},"_links":{"self":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001617","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/comments?post=55001617"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55001617\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55001617"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55001617"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55001617"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}