Um berechnete Zahlen usw. in eine Zeichenkette einzufügen,
benötigt man Konvertierungsfunktionen, die aus einer Zahl
eine Zeichenkette machen. Dazu ein Beispiel:
(setq i 1)
(repeat 100
(princ
(strcat "\nDies ist der "
(itoa i)
". Schleifendurchlauf"
)
)
)
(itoa ...) ist eine Funktion, um aus einer Integerzahl
eine Zeichenkette zu machen. Der name ist die Abkürzung für
'integer to ASCII'. Die gegenteilige Funktion
(atoi) macht
aus einer Zeichenkette eine Zahl:
(itoa 64) > "64"
(atoi "100") > 100
; es wird nur konvertiert, was sich
; auch konvertieren lässt!
(atoi "100.0") > 100
(atoi "100x") > 100
; und das hier kann eine sehr
; gefährliche Fehlerquelle sein!
(atoi "Gar keine Zahl") > 0
Etwas aufwendiger ist das Konvertieren von Realzahlen in
Zeichenketten, da es verschiedene Darstellungsmöglichkeiten
gibt und meistens auch nicht die grösstmögliche Genauigkeit
der Darstellung gewünscht ist. Da die Umwandlung in eine
Zeichenkette in der Regel der Bildschirmausgabe dient, wird man
sich mit einer Darstellung von ein oder zwei Kommastellen begnügen.
Die Funktion zur Umwandlung von Realzahlen in Zeichenketten
heisst
(rtos ...) und erhält drei Argumente. Das erste
Argument ist die Realzahl, das zweite gibt den Darstellungsmodus
an. Der Wert 2 bedeutet dezimale Darstellung. Andere Werte (z.B.
wissenschaftliche Darstellung) entnehmen Sie bitte der AutoCAD-Hilfe.
Das dritte Argument gibt schliesslich bei der Dezimaldarstellung
die Anzahl der gewünschten Dezimalstellen an:
(rtos pi 2 2) > 3.14
; Dezimal, zwei Kommastellen
(rtos pi 4 8) > 3 9/64
; Fuss- und Zolldarstellung
Umgekehrt (Konvertierung einer Zeichenkette in eine Realzahl)
ist der Weg wesentlich einfacher. Die Funktion heisst (atof) und
hat nur ein Argument:
(atof "5.50") > 5.5
Wir haben schon im Beispiel für
(atoi) folgendes gesehen:
Sowohl
(atoi) als auch
(atof) führen die Konvertierung
nur bis zum ersten nicht mehr konvertierbaren Zeichen durch, die
weiteren Zeichen werden einfach ignoriert. Das bedeutet, dass Sie
von diesen Funktionen niemals eine Fehlermeldung erhalten werden,
wenn das Argument eine Zeichenkette ist, in der gar keine Ziffern
enthaltensind. Selbst wenn die Zeichenkette nicht ein einziges
interpretierbares Zeichen enthält, erhalten Sie 0 zurück. Beispiele:
(atoi "123, Schlamperei") > 123
(atoi "qwertzuiopü") > 0
(atof "") > 0.0
Dies kann manchmal nützlich sein, es verursacht aber auch
Schwierigkeiten, da inhaltlich gesehen ein nicht vorhandener Wert
etwas ganz anderes ist als der Wert 0.
AutoLisp kennt keinen Datentyp ASCII-Zeichen oder etwas
Vergleichbares. Wenn wir also mit einzelnen Zeichen operieren wollen,
müssen wir dies tun, indem wir eine ganz normale Zeichenkette benutzen.
In selbstgeschriebenen Funktionen müssen wir also ggf. prüfen, ob ein
String ein einzelnes Zeichen enthält oder mehrere.
Es stehen aber noch zwei Konvertierungsfunktionen für Zeichen zur
Verfügung:
(chr) konvertiert eine Zahl in das entsprechende
ASCII-Zeichen.
(ascii) tut das Gegenteil, wenn Sie dieser
Funktion ein Zeichen übergeben, erhalten Sie den ASCII-Code zurück.
Dabei ist
(ascii) ein Beispiel dafür, wie man, da es keinen
Datentyp 'Zeichen' gibt, in solchen Fällen verfahren muss:
(chr 75) > "K"
(ascii "Käsebrot") > 75
(ascii "K") > 75
Wie Sie sehen, wird nur das erste Zeichen verarbeitet, die anderen
werden einfach ignoriert. Ganz Trickreiche schreiben statt
(substr str 1 1) auch schon mal
(chr(ascii str)).
Zum Schluss sollten wir noch auf die Funktion
(read) zu
sprechen kommen. Diese Funktion konvertiert eine Zeichenkette in
ein Symbol oder eine Liste. Das Ergebnis hängt davon ab, wie die
Zeichenkette aufgebaut ist. Wenn es sich um einen geklammerten
Ausdruck handelt, werden lediglich die Anführungszeichen entfernt
und wir erhalten eine Liste. Bei ungeklammerten Ausdrücken
wird der Inhalt in ein Symbol umgewandelt. Dabei ist aber zu
beachten, dass Symbole manche Zeichen nicht enthalten dürfen. Eine
Zeichenkette wird daher nur bis zum ersten Auftreten eines solchen
Zeichens konvertiert.
(read "(1 2 3)") > (1 2 3)
(read "erlaubt") > ERLAUBT
(read "verbotenes Zeichen") > VERBOTENES
Das verbotene Zeichen im letzten Beispiel war natürlich die
Leerstelle. Sie wissen ja, dass Leerstellen sowie die Zeichen
. ( ) " nicht in Symbolnamen vorkommen dürfen. Die
(read)-Funktion ist übrigens keine Spielerei, sondern
eine ganz wichtige Funktion, die Sie immer wieder brauchen werden.
Das hat seinen Grund darin, dass jede Eingabe an Lisp, die nicht
über die Tastatur erfolgt oder mit (load) geladen wird, sondern
statt dessen aus einer ASCII-Datei gelesen wird, zunächst einmal
den Datentyp Zeichenkette hat.
Wenn wir z.B. (mit den später noch zu besprechenden Funktionen
zur Ein- und Ausgabe in/aus Dateien) Daten aus Dateien einlesen,
wird es immer nötig sein, numerische Angaben mit
(atoi)
oder
(atof) zu konvertieren. Enthalten diese Dateien dann
auch Symbole oder Listen (was natürlich auch Programmcode mit
einschliesst), dann müssen wir
(read) anwenden. Diese
Situation kommt gar nicht so selten vor.
Die gesamte DCL-Programmierung (Dialogboxen) in AutoLISP ist
z.B. auf den Mechanismus aufgebaut, dass in den Definitionsdateien
für die Dialoge Lisp-Codefragmente als Zeichenketten abgelegt
bzw. übergeben werden werden.
Da Sie nun die Funktion
(read) kennen, darf Ihnen auch
(eval) nicht vorenthalten werden.
(eval) hat weder
direkt etwas mit Zeichenketten zu tun, noch ist es eine
Konvertierungsfunktion. Wir erörtern
(eval) deshalb an
dieser Stelle, weil diese Funktion häufig - aber nicht immer -
im Zusammenhang mit
(read) benutzt wird.
(eval) evaluiert einfach sein Argument - sonst passiert nichts.
Sie werden sich fragen, wozu das gut sein soll - schliesslich werden
doch sowieso ständig alle Ausdrücke evaluiert. Das ist richtig, es
kann aber vorkommen, dass Sie mehrere Stufen der Evaluation auf
einmal brauchen. Dazu ein Beispiel:
(setq a 0.75) => 0.75
(setq b 'a) => A
(setq c 'b) => B
Nach diesen Zuweisungen müssen wir die Variable c doppelt
evaluieren, um an den Wert 0.75 zu kommen.
(eval (eval c)) => 0.75
Um diesem etwas kniffligen Ablauf besser auf die Spur zu kommen,
führen wir die Evaluation noch einmal in Stufen durch:
c => B
(eval c) => A
(eval (eval c)) => 0.75
Aus diesem Beispiel wird deutlich, dass die Funktion
(eval)
ihr Argument nicht quotiert, d.h. der Ausdruck
(eval c)
führt bereits zu einer doppelten Evaluation. Für die eine Evaluation
sorgt allein die Tatsache, dass ein nicht quotiertes Argument an
eine Funktion übergeben wird, für die zweite Evaluation sorgt der
(eval)-Aufruf. Bei der Eingabe von
(eval(eval c))
laufen also 3 Evaluationen ab, nicht 2!
Um wieder zum eigentlichen Thema, nämlich den Zeichenketten und der
Konvertierung zurückzukehren, hier noch ein Beispiel für eine öfters
einmal angewandte Technik: Symbolnamen werden zunächst als Zeichenketten
erzeugt, dann in Symbole umgewandelt, und schliesslich wird ihnen
ein Wert zugewiesen. Der folgende Ausdruck erzeugt 10 Variablen mit
den Namen
variable0 bis
variable9 und weist ihnen ihre
Nummer als Wert zu:
(setq i 0)
(while(< i 10)
(set(read(strcat "variable" (atoi i)))i)
(setq i(1+ i))
)
!variable0 > 0
!variable5 > 5
!variable9 > 9
!variable10 > nil
Übungsaufgaben
-
Schreiben Sie eine Test-Funktion, die feststellt, ob eine
Zeichenkette Umlaute oder 'ß' enthält
-
Schreiben Sie eine Funktion, die alle in einer Zeichenkette
vorkommenden Ziffern zählt
-
Schreiben Sie eine Funktion, die die Quersumme einer
Integerzahl berechnet
-
Schreiben Sie eine Funktion (safe-atoi ...), die nil
zurückgibt, wenn keine einzige umzuwandelnde Ziffer vorgefunden
wurde. Ansonsten soll sie sich genauso wie (atoi ...)
verhalten.