Ein paar Worte vorabHome   Letzte MeldungenNews   Index der Kapitel und der besprochenen FunktionenIndex   Wer ich bin, warum ich diese Seiten mache, KontaktImpressum   Ich freue mich über jeden Eintrag im Gästebuch!Gästebuch   Einige Links zu anderen AutoLisp-SeitenLinks   Copyrights und DisclaimerRechts
Hier können die kompletten Seiten als ZIP-File heruntergeladen werden!

Funktionen für komfortables Arbeiten mit Zeichenketten String-Tango
Noch mehr Funktionen für komfortableres Arbeiten mit Zeichenketten Kettenhunde
strtok zerlegt Zeichenketten anhand eines Trennzeichens Tock-Tock
Arbeiten mit Datum und Zeit in AutoLisp Zeitlos...
Dotted pairs - wie man den Programmabbruch verhindert Gepunktet?
Neue Funktionen für die Listenbearbeitung Strukturtapete
Weitere neue Funktionen für die Listenbearbeitung Listen to me!
Lambda expressions - dasSalz in der Suppe Lambada
Lambda expressions anhand eines Praxisbeispiels Unter der Erde
Where und whereever erleichtern den Umgang mit Listen Quo vadis?
Rekursion - Funktionen, die sich selbst aufrufen Katzenschwanz
Ein äusserst wichtiger Prototyp für Funktionen Nix passiert
Wo das lineare mapcar am Ende ist Tiefer rein!
Über Effekte und Neben(Seiten-)Effekte von Funktionen Seitensprünge
Die Namensräume (Sichtbarkeit) von Variablen Raumwunder
Let dient zur Schaffung kleinerer Namensräume Lass mal...
Sukzessive Verarbeitung von Listenresten mit mapcdr Der Bruder
Was in AutoLisp einfach nicht machbar ist (Teil I) Beschränkt
Was in AutoLisp einfach nicht machbar ist (Teil II) Limited Edition
Nicht mit Effekten arbeiten, sondern direkter Daten-Änderung Destruktiv
Sequenzielles vs. paralleles Abarbeiten von Argumenten Parallelwelten
Über den Umgang mit Funktionsschablonen Erwachet!
Ein Praxiskapitel über Auswahlsätze, Attribute, wcmatch und mehr Durch die Brust
Die Farben des AutoCAD Color Index und ihre RGB-Werte Alles so schön
Hier laufen die Fäden zusammen: Viele Konzepte vereint Lapsus Lispuli
Ein Spiel als Beispiel für lernfähige Funktionen Zug um Zug
Errorhandling in AutoLisp - Teil 1 Alles valsch!
Errorhandling in AutoLisp - Teil 2 Und foll Feler!
Regular expressions in AutoLisp - Teil 1 Menstruation?
Regular expressions in AutoLisp - Teil 2 Hasso, such!
Regular expressions in AutoLisp - Teil 3 Ersatzteile
Regular expressions in AutoLisp - Teil 4 Perlentaucher


Zum Einsteiger-Tutorial

Zu den ActiveX-Seiten

Meine Private HP mit Fotos, Gedichten, Musik und Postkartenversand

Mein Online-Lexikon der Fotografie

Mein völlig abgedrehtes Reisebüro

Meine bonbonfarbene Gedichteseite










Die Arbeit mit regulären Ausdrücken teilt sich in zwei Kategorien ein: Zum Einen ist da das Suchen, bei dem der Text durchsucht, aber nicht verändert wird. Zum Anderen gibt es das Suchen & Ersetzen, das bereits im vorigen Kapitel angesprochen wurde. In beiden Fällen braucht man ein Suchmuster, im zweiten Fall auch ein Ersetzungsmuster.

Zunächst werden wir uns mit dem Suchmuster befassen und ein paar einfache Regeln kennenlernen. Damit das Ganze nachvollziehbar wird, schlage ich vor, die folgenden kleinen Übungen direkt auf der AutoCAD-Kommandozeile nachzuvollziehen. Dazu muss natürlich die PCRE-Funktionalität erst einmal in Lisp geladen sein. Das ist ganz einfach: Laden Sie die Datei pcre.arx (33 kB) hier herunter und legen Sie sie irgendwo ab, wo AutoCAD sie findet. Dann muss sie nur noch mit dem AutoCAD-Befehl APPLOAD oder der Lisp-Funktion (arxload "pcre2006.arx") geladen werden. Die Datei lässt sich übrigens auch problemlos wieder entladen, was ja bei ARX-Anwendungen oft nicht der Fall ist.

Nach dem Laden steht uns eine einzige neue Lisp-Funktion namens (pcre-match ...) zur Verfügung. Diese Funktion erwartet 2 oder 3 Argumente:
  1. das Suchmuster
  2. die zu durchsuchende Zeichenkette
  3. optionales Argument: eine weitere Zeichenkette für die Suchoptionen
Um das dritte Argument machen wir uns erst einmal keine Gedanken, wir lassen es einfach weg. Tippen wir doch einmal eine ganz kleine Suchanfrage ein. Als Suchstring verwenden wir das Wort "Donaudampfschifffahrtsgesellschaft", und wir wollen wissen, ob der Buchstabe m in diesem Wort vorkommt:
(pcre-match "m" "Donaudampfschifffahrtsgesellschaft") => ((7 1))
                  
So eine einfache Frage hätte sich auch mit wcmatch klären lassen:
(wcmatch "Donaudampfschifffahrtsgesellschaft" "*m*") => T
                  
Man beachte, dass wcmatch immer nur zwei vollständige Zeichenketten miteinander vergleicht, von denen die zweite Stellvertreterzeichen enthalten kann - deshalb die Sternchen vor und nach dem Buchstaben m. Das PCRE-Matching ist völlig anders: Es prüft nicht, ob der gesamte String und das Suchmuster 'matchen', sondern es sucht passende Stellen und gibt deren Position zurück. Die Rückgabe ((7 1)) ist so zu interpretieren: Die Position im String ist 7, und die Fundstelle ist 1 Zeichen lang.

Nehmen wir einmal an, es soll ein Text daraufhin untersucht werden, ob er mit der alten oder der neuen Rechtschreibung konform ist (gemäß der alten Rechtschreibung schrieb man ja "Schiffahrt" mit nur zwei f). Natürlich ist eines auf Anhieb klar, dass man nach 3 f so suchen kann:
(pcre-match "fff" "Donaudampfschifffahrtsgesellschaft") => ((14 3))
                  
Ach ja, hier wird immer ab 0 gezählt! Der 15. Buchstabe ist also das erste der 3 f. Sucht man jedoch nach 2 f, passiert folgendes: Logischerweise enthalten 3 f immer 2 f, deswegen wird eine Suche nach einem Doppel-f die gleiche Fundstelle anzeigen:
(pcre-match "ff" "Donaudampfschifffahrtsgesellschaft") => ((14 2))
                  
Um ganz explizit ein Doppel-f zu suchen, müssen wir eine andere Technik anwenden und das Suchmuster präzisieren:
(pcre-match "[^f]ff[^f]" "Donaudampfschifffahrtsgesellschaft") => nil
                  
Die eckigen Klammern bedeuten normalerweise "ein Zeichen aus dieser Menge", ist allerdings ein Caret ^ am Anfang, wirkt dieses als Negationszeichen, es heisst nun "ein Zeichen, das nicht in dieser Menge ist". [aeiou] bedeutet also "ein Vokal in Kleinbuchstaben", [^AEIOU] heisst dann, bei Licht betrachtet, "ein großgeschriebener Nicht-Vokal". Das muss noch nicht unbedingt ein Konsonant sein, denn es könnte ja eine Ziffer, ein Satzzeichen oder sonst etwas sein. Kurzum, [^f] bedeutet also "alles, nur kein kleines f". Wir suchen also nach einem Zeichen, das kein f ist, dann zwei f, dann wieder ein anderes Zeichen. Natürlich enthält das Riesenwort in neuer Rechtschreibung kein Doppel-f, sondern ein Dreifach-f. Deswegen bekommen wir eine leere Liste (nil) zurück. Die Gegenprobe mit der alten Schreibweise:
(pcre-match "[^f]ff[^f]" "Donaudampfschiffahrtsgesellschaft") => ((13 4))
                  
Die nächste Übung ist wieder etwas komplexer: Es soll in diesem Wort (wieder mit 3 f gemäß der neuen Rechtschreibung) eine Stelle gesucht werden, an der 6 Konsonanten aufeinander folgen. Das lässt sich so realisieren:
(pcre-match "[bcdfghjklmnpqrstvwxyz]{6}" "Donaudampfschiffahrtsgesellschaft")
                  
Die Angabe {n} nach einem Ausdruck beutet also n-mal, das erspart es uns, den Ausdruck n-mal zu schreiben. Es gibt aber noch weitere Abkürzungen, z.B. die Schreibweise [0-9] für eine Ziffer, [a-z] für Kleinbuchstaben. [a-zA-Z] für alle Buchstaben usw. Ferner gibt es dann noch folgende Zeichen:
  • \d steht für eine Ziffer (entspr. [0-9])
  • \s steht für ein Whitespace-Zeichen, also Leerzeichen, Tabulator, Zeilenumbruch usw. (entspr. [ \t\n\r\f])
  • \w steht für ein Wort-Zeichen (entspr. [a-zA-Z0-9_])
Diese Abkürzungen können durch das Verwenden eines Großbuchstabens negiert werden:
  • \D ist also eine Nicht-Ziffer [^0-9]
  • \S steht für ein Nicht-Whitespace-Zeichen [^ \t\n\r\f]
  • \W steht für ein Nicht-Wort-Zeichen [^a-zA-Z0-9_]
Noch wesentlicher ist es aber, dass Sie die Bedeutung der folgenden Zeichen verstehen:
  • . irgendein Zeichen
  • * das Zeichen kann 0 mal vorkommen, aber auch öfters
  • + das Zeichen muss mindestens einmal vorkommen
  • ? das Zeichen kann 0 oder 1 mal vorhanden sein
  • \ das folgende Zeichen als Literal verwenden
Ein paar Beispiele:
  • a+ sucht nach mindestens einem a, also a oder aa oder aaa ...
  • a.b.c sucht nach abc mit irgendwas dazwischen, z.B. axbxc oder a_b_c
  • A.*E.*G würde z.B. "Ausschalten, Einschalten, Garantiefall" finden, aber auch AEG
  • A.+E.+G würde zwar "Ausschalten, Einschalten, Garantiefall" finden, aber nicht AEG
  • \d sucht nach genau einer Ziffer
  • \d+ sucht nach mindestens einer Ziffer
  • \d* sucht nach mindestens keiner Ziffer (!)
  • r e sucht nach einer Stelle im Text, wo ein Wort mit r aufhört und das nächste mit e anfängt
  • r +e dasselbe, aber es können auch mehrere Leerstellen dazwischen sein
  • r *e das reagiert auch auf 'reagieren', weil zwischen dem r und dem e mindestens keine Leerstelle ist
Kehren wir zurück zur eigentlichen Aufgabe, die im vorigen Kapitel formuliert wurde: Es soll eine Dezimalzahl innerhalb einer Zeichenkette gefunden werden. Wenn wir als Beispiel den String "Ein Text, in dem die Zahl 123,45 zu finden ist", dann können wir das Suchmuster so in Worten definieren:

Zunächst eine beliebige Anzahl von Zeichen, dann eine Leerstelle, dann mindestens eine Ziffer, anschließend ein Komma, dann wieder mindestens eine Ziffer, eine Leerstelle und schließlich wieder beliebige Zeichen bis zum Ende. Und so sieht unser Suchmuster nun in der Praxis aus:

.* \d+,\d+ .*

Wir müssen allerdings, um dieses Muster austesten, wie immer in Lisp die Backslashs doppelt schreiben (der Backslash wird sonst mit dem darauf folgenden Zeichen als Steuerzeichen gewertet).
(pcre-match ".* \\d+,\\d+ .*" "Ein Text, in dem die Zahl 123,45 zu
finden ist") => ((0 46))
                  
Die Rückgabe zeigt uns an, dass unser String von Anfang bis Ende dem Suchmuster entspricht. Damit wissen wir aber noch immer nicht, *wo* sich die Zahl befindet. Um das zu klären, gruppieren wir mit runden Klammern:
(pcre-match "(.* )(\\d+,\\d+)( .*)" "Ein Text, in dem die Zahl 123,45
zu finden ist") => ((0 43) (0 26) (26 6) (32 11))
                  
Für jede Gruppierung, also jede von uns eingefügte runde Klammer, erhalten wir eine weitere Unterliste mit einer Position und einer Länge. Man kann das Ergebnis also so interpretieren: Von Pos 0 bis 25 (Länge 26) wurden die beliebigen Zeichen sowie die Leerstelle vor der Zahl gefunden. Die 6 Zeichen ab Pos. 26 sind die Zahl einschl. Komma, und ab Pos. 32 folgen wieder eine Leerstelle und beliebige Zeichen.

Die Anzahl der Klammerpaare ist übrigens in der hier vorliegenden Implementation auf 9 begrenzt, das Ergebnis enthält maximal 10 Unterlisten (die erste ist immer der 'whole match', weitere 9 können die Gruppen-Positionen und -längen beinhalten). Denken Sie immer daran, dass sich der 'whole match' an der Länge des ausgewerteten Suchmusters orientiert, niemals aber an der Länge der zu durchsuchenden Zeichenkette! Allerdings kann der 'whole match' aber niemals länger als die Zeichenkette werden.

Damit haben Sie nun einen kleinen Einblick in das Suchen mit regulären Ausdrücken bekommen. Im nächsten Kapitel werden wir uns mit dem Ersetzen befassen.