Die im vorigen Kapitel besprochene Funktion (pcre-match) dient ausschließlich
zum Suchen. Wollen wir Teile einer Zeichenkette ersetzen, beötigen wir eine
weitere Funktion namens (pcre-sr), die allerdings im Gegensatz zur
match-Funktion nicht in C geschrieben ist, sondern kurz und bündig in Lisp.
Natürlich müssen Sie die Funktion erst einmal haben, Sie können sie hier
herunterladen:
pcre.lsp(1 kb). (pcre-sr) ist übrigens
die einzige Funktion in dieser Datei, und mehr benötigen Sie dann auch nicht
mehr. Laden Sie die Lisp-Datei (mit APPLOAD oder (load"pcre.lsp")) zusätzlich
zur ARX-Datei pcre2006.arx.
(pcre-sr ...) muss immer mit genau vier Argumenten aufgerufen werden:
- das Suchmuster
- das Ersetzungsmuster
- die Zeichenkette, in der gesucht bzw. ersetzt werden soll
- mögliche Optionen - das Argument ist aber nicht optional!
AutoLisp erlaubt keine optionalen Funktionsargumente. Anders als bei
(pcre-match) können wir also das letzte Argument nicht weglassen! Da wir aer
im Rahmen dieser Übungen keine Optionen brauchen, verwenden wir als 4.
Argument immer einen Leerstring.
Das Ersetzungsmuster unterscheidet sich deutlich vom Suchmuster: hier gelten
ganz andere Regeln. Ein Punkt ist z.B. im Ersetzungsmuster immer ein Punkt und
nicht ein Stellvertreter für ein beliebiges Zeichen. Das wichtigste ist aber
eine neue Gruppe von Stellvertretern, die "\0", "\1" usw. bis "\9" heissen.
Diese Stellvertreter (die in Lisp natürlich wieder mit doppeltem Backslash
geschrieben werden müssen) stehen für die Unterlisten im Ergebnis von
(pcre-match), das von (pcre-sr) aufgerufen wird. Ich möchte das an einem
Beispiel verdeutlichen:
(pcre-sr
"(.*)(Äpfel)(.*)(Birnen)(.*)"
"\\1\\4\\3\\2\\5"
"Diese Äpfel schmecken besser als jene Birnen und Kirschen"
""
)
=> "Diese Birnen schmecken besser als jene Äpfel und Kirschen"
Wir müssen uns also gar nicht mit den einzelnen Positionen und Längen des
Suchergebnisses befassen. Hier wurden die Teilzeichenketten einfach in einer
neuen Reihenfolge angeordnet. Natürlich kann das Ersetzungsmuster auch
Literale enthalten:
(pcre-sr
"(.*)(Äpfel)(.*)(Birnen)(.*)"
"\\1frischen \\4\\3verschrumpelten \\2"
"Diese Äpfel schmecken besser als jene Birnen"
""
)
=> "Diese frischen Birnen schmecken besser als jene
verschrumpelten Äpfel"
Damit haben wir nun alle Techniken zusammen, die wir für die Lösung der
gestellten Aufgabe (Hinzuaddieren einer Konstante zu Zahlen, die in Texte
eingebettet sind) benötigen. Zur Vorgehensweise aber noch ein paar
Erläuterungen: Zuerst müssen wir die Zahl aus dem Text extrahieren. Um die
Konstante zu addieren, muss sie zwischenzeitlich in den Lisp-Datentyp Real
umgewandelt werden. Nach der Addition wird sie wieder als String dargestellt
und kann mit (pcre-sr) die alte Zahl in der Zeichenkette ersetzen. Diese
Aufgabe lässt sich locker mit zwei Lisp-Zeilen erledigen, die in der Funktion
(add-const) zu finden sind - alles andere im nun folgenden Code, nämlich die
ganze Funktion (solution-sample), dient nur dazu, um Testdaten bereitzustellen
und diese in einer Schleife zu durchlaufen.
(if(null pcre-sr)(load"pcre.lsp"))
(defun solution-sample( / lines number result)
(setq lines
(list
"Irgendwas irgendwie irgendwo z.B. die Zahl 123.45 oder so!"
"Irgendwas irgendwie irgendwo z.B. die Zahl 678.9 oder so!"
"Irgendwas irgendwie irgendwo z.B. die Zahl 22.999999 oder so!"
)
)
(foreach line lines
(print(add-const line 0.77))
)
(princ)
)
(defun add-const(line const / number)
(setq number(pcre-sr " (\\d+\\.\\d+) " "\\1" line ""))
(pcre-sr "(.*) (\\d+\\.\\d+) (.*)" (strcat"\\1 "
(rtos(+(atof number)const)2 2)" \\3") line "")
)
Die Testfunktion nimmt allerdings wenig Rücksicht auf die vorher tatsächlich
vorhandene Anzahl von Nachkommastellen. Im Beispiel sind mal zwei, mal eine
und mal sechs. Nach dem Ersetzen haben aber alle Zahlen genau zwei
Nachkommastellen, bedingt durch den (rtos)-Parameter. In der Regel wird jedoch
eine Datei mit Geländedaten sowieso eine feste Anzahl von Nachkommastellen
verwenden, sodass das Problem gar nicht zum Tragen kommt. Sollte das anders
sein, muss man eben doch ein wenig mehr Lisp-Code schreiben: Man stellt
einfach die Anzahl der Nachkommastellen in der Original-Zeichenkette fest und
übergibt diese Zahl als Parameter an (rtos). Das sollte nicht allzu schwierig
sein.
Ein letzter Hinweis noch: In der ersten der beiden relevanten Zeilen könnte
man natürlich auch (pcre-match) verwenden. Dann müsste man allerdings die Zahl
anschließend mit (substr) aus der Zeichenkette auslesen. Die Anwendung von
(pcre-sr) erspart und diese Einzelheiten. Letztlich führen aber viele Wege
nach Rom, und es sind jede Menge Möglichkeiten vorstellbar, wie diese Zeilen
aussehen könnten.
Noch ein ganz kurzes Wort den Dingen, die ich übergangen habe. Da sind z.B.
die vier möglichen Options-Flags zu nennen:
- i (wie 'insensitive') bedeutet, dass auf Groß- bzw. Kleinschreibung
keine Rücksicht genommen wird.
- s (wie 'single line') Ein Text mit Zeilenumbrüchen soll wie eine
einzige Zeile behandelt werden
- m (wie 'multiple lines') Ein Text mit Zeilenumbrüchen soll als
Mehrzeilentext behandelt werden
- x (wie 'eXtended pattern') sorgt dafür, dass Whitespace im
Suchmuster ignoriert wird. Dadurch kann man das Suchmuster z.B.
in mehrere Zeilen zerlegen, um die Lesbarkeit zu erhöhen
Für den Anfänger wird allerdings zunächst nur das Flag i von Bedeutung sein.
Ein Beispiel dazu:
(pcre-match "M" "Donaudampfschifffahrtsgesellschaft" "i") => ((7 1))
Auch auf Literale wurde kaum eingegangen. So ist z.B. der Punkt der
Stellvertreter für ein beliebiges Zeichen. Will man aber wirklich nach einem
Punkt suchen, so muss man diesem einen Backslash voranstellen, damit er als
Literal gewertet wird. Und natürlich muss man den Backslash doppelt schreiben!
Im nächsten Kapitel werde ich noch auf die PCRE-Bibliothek eingehen. Dort
finden sich auch Hinweise, wo man mehr über die Anwendung von regular
expressions erfährt.