;| Diese Datei beinhaltet die Definition von Funktionen zur Zeichenkettenbearbeitung. diese Funktionen stammen größtenteils aus dem Autolisptutorial von MAPCAR. (www.mapcar.de). Die meisten Erläuterungstexte stamme auch aus diesem Tutorial. Die Kurzbeschreibung wurde von Stefan Wickel eingefügt. Einzelne Funktionen wurden von Stefan Wickel eingefügt. Folgende Funktionen sind enthalten: (defun str-list(str / ls i) ;zerlegt eine Zeichenkette in eine Liste mit einzelnen Buchstaben (defun str-ltrimset(str cset / i) ;entfernt die Zeichen die in cset enthalten sind vom Anfang der zeichenkette (bis zum ersten auftreten eines anderen Zeichens) (defun str-rtrimset(str cset / len) ;entfernt die Zeichen die in cset enthalten sind vom ENDE der zeichenkette (bis zum ersten auftreten eines anderen Zeichens) (defun str-ltrim(str / ) ;entfernt "Whitespaces" vom Anfang der Zeichenkette (defun str-rtrim(str / ) ;entfernt "Whitespaces" vom Ende der Zeichenkette (defun str-trim(str / ) ;entfernt white space an beiden Enden (defun str-trimz(str / ) ;entfernt white space an beiden Enden sowie führende Nullen (defun str-lpadchar(str nl ch / ) ;füllt links auf mit Zeichen ch bis Länge nl, zu lange Strings werden abgeschnitten (defun str-rpadchar(str nl ch / ) ;füllt rechts auf mit Zeichen ch bis Länge nl, zu lange Strings werden abgeschnitten (defun str-cpadchar(str nl ch / len) ;füllt auf mit Zeichen ch bis Länge nl Zentriert, zu lange Strings werden abgeschnitten (defun str-lpad(str nl / )(str-lpadchar str nl " ")) ;füllt links mit Leerzeichen, zu lange Strings werden abgeschnitten (defun str-rpad(str nl / )(str-rpadchar str nl " ")) ;füllt rechts mit Leerzeichen, zu lange Strings werden abgeschnitten (defun str-cpad(str nl / )(str-cpadchar str nl " ")) ;füllt zentriert mit Leerzeichen, zu lange Strings werden abgeschnitten (defun str-lpadz(str nl / )(str-lpadchar str nl "0")) ;füllt links mit Nullen, zu lange Strings werden abgeschnitten (defun str-rpadz(str nl / )(str-rpadchar str nl "0")) ;füllt rechts mit Nullen, zu lange Strings werden abgeschnitten Die folgenden Funktionen nehmen nur Einzelzeichen an ----------------------------------------------------- (defun isalnum? (c / ) ;testet ob alphanumerisch (Ascii 48 bis 57, 65 bis 90 oder 97 bis 122) (defun isalpha?(c / ) ;testet ob Buchstabe (Ascii 65 bis 90 oder 97 bis 122) (defun isascii?(c / ) ;testet ob einfacher Asciibereich (Ascii kleiner 128) (defun iscntrl?(c / ) ;testet ob Steuerzeichen (Ascii kleiner 30) (defun isdigit?(c / ) ;testet ob numerisch (Ascii 48 bis 57) (defun islower?(c / ) ;testet ob Kleinbuchstabe (Ascii 97 bis 122) (defun isprint?(c / ) ;testet ob Druckbares Zeichen (Ascii 32 bis 126) (defun ispunct?(c / ) ;testet ob Interpunktionszeichen !"'(),.:;?[]{} (defun isspace?(c / ) ;testet ob whitespace (Leerzeichen, Zeilenwechsel, Wagenrücklauf, Tab) (defun isupper?(c / ) ;testet ob Großbuchstabe (Ascii 65 bis 90) (defun str-pos(str c / i l ls lc) ;sucht den String c in str und gibt die Position zurück, Groß- und Kleinschreibung werden berücksichtigt (defun str-ipos(str c / ) ;sucht den String c in str und gibt die Position zurück, Groß- und Kleinschreibung werden ignoriert (defun str-lastpos(str c / i l ls lc) ;sucht den String c in str und gibt letzte Position zurück, Groß- und Kleinschreibung werden berücksichtigt (defun str-lastipos(str c / ) ;sucht den String c in str und gibt letzte Position zurück, Groß- und Kleinschreibung werden ignoriert (defun str-posset(str cset / i) ;sucht ein Zeichen des Strings cset in str (defun str-insert(str neu pos / ) ;fügt eine Zeichenkette an der angegebenen Position in einen anderen String ein (defun str-remove(str pos n / ) ;entfernt an der angegebenen Position eine bestimmte Menge von Zeichen (defun str-subst(str neu alt / i la str2) ;ersetzt einen Teilstring innerhalb eines Strings durch einen anderen String (defun str-count(str tok / res) ;zählt, wie oft das Token tok im String str vorkommt (defun str-icount(str tok / )'( ;zählt, wie oft das Token tok im String str vorkommt unabhängig von der Groß- und Kleinschreibung (defun str-div(str c / i l ) ;teilt den String str an der Stelle in zwei Teile, an dem das Zeichen c erstmals auftritt (defun str-divset(str cset / i l) ;teilt den String str an der Stelle in zwei Teile, an dem ein Zeichen aus cset erstmals auftritt (defun str-tok(str c / tmp) ;teilt den String str an den Stellen, an denen das Zeichen c auftritt (auch mehrfach) (defun str-tokset(str cset / tmp) ;teilt den String str an den Stellen, an denen ein Zeichen aus cset auftritt (auch mehrfach) (defun str-untok ls c / ) ;fügt eine Liste ls voll Strings zu einem String mit Trennzeichen c zusammen (defun str-div-pos (str poslist / strlist von pos) ;teilt den String str an den in poslist angegebenen Stellen und gibt eine Liste mit Strings zurück AutoLisp bietet zur Verarbeitung von Zeichenketten nur die Funktionen (substr ...), (strcat ...), (strcase ...) und (strlen ...) an. Das erscheint auf den ersten Blick sehr mager, erweist sich aber bei näherer Betrachtung als durchaus ausreichend. Nimmt man noch die Funktionen (chr ...) und (ascii...) hinzu, lässt sich damit das gesamte Spektrum der anfallenden Aufgaben abdecken. Allerdings ist das Arbeiten mit so wenigen Grundfunktionen in keiner Weise komfortabel. Damit man nicht das Rad immer wieder neu erfinden muss, sollte man über eine Erweiterungsbibliothek verfügen, die den Bedarf an Komfort abdeckt. Die Vorteile bei der Benutzung einer solchen Funktion liegen auf der Hand: Code kann schneller geschrieben werden, da er kürzer wird. Mit der Verkürzung des Codes ensteht natürlich auch eine größere Übersichtlichkeit. Zusätzlich wird der Code auch sicherer, wenn gewährleistet ist, dass die Bibliotheksfunktionen fehlerfrei sind. Ich werde hier auf drei grundlegende Aufgabenstellungen eingehen: * Bereinigen von Zeichenketten: Entfernen oder Hinzufügen von Leerstellen, white space usw. * Verändern von Zeichenketten: Suchen und Ersetzen von Teilen und Ähnliches * Aufteilen und Zusammenfügen: Tokenizer (Teilzeichenketten als Listen verarbeiten) und wieder zusammenfügen Hinzu kommen noch einige Prädikat-Funktionen (Testfunktionen für Zeichen), die von den eigentlichen Bearbeitungsfunktionen u.U. benötigt werden. Fangen wir also mit der ersten Aufgabengruppe an: Sehr oft müssen Leerstellen oder white space am Anfang oder am Ende eines Strings entfernt werden. Um größte Flexibilität zu erreichen, definieren wir zunächst eine Funktion, die möglichst allgemein ist und mit allem fertig wird. Sie bekommt nach dem zu bearbeitenden String noch ein weiteres Argument, nämlich einen zweiten String, der alle Zeichen enthält, die entfernt werden sollen. Dieser zweite String wird also als set bzw. Liste von Einzelzeichen aufgefasst. Um aber auf diese Einzelzeichen zugreifen zu können, benötigen wir zu allererst die Hilfsfunktion (str-list ...), die einen String in eine Liste von Einzelzeichen zerlegt. Wir benötigen diese Liste, um auf die Einzelzeichen des zweiten Arguments zugreifen zu können. |; (defun str-list(str / ls i) ;zerlegt eine Zeichenkette in eine Liste mit einzelnen Buchstaben (setq i 1) (repeat(strlen str) (setq ls(cons(substr str i 1)ls)) (setq i(1+ i)) ) (reverse ls) ) ;;;Anwendungsbeispiel: ;;; ;;;(str-list "String") => ("S" "t" "r" "i" "n" "g") ;;; ;;;Jetzt können wir unsere universell verwendbaren Trim-Funktionen schreiben: (defun str-ltrimset(str cset / i) ;entfernt die Zeichen die in cset enthalten sind vom Anfang der zeichenkette (bis zum ersten auftreten eines anderen Zeichens) (if(>(strlen str)0) (progn (setq cset(str-list cset)) (setq i 1) (while(member(substr str i 1)cset) (setq i(1+ i)) ) (substr str i) ) ) ) (defun str-rtrimset (str cset / len) ;entfernt die Zeichen die in cset enthalten sind vom ENDE der zeichenkette (bis zum ersten auftreten eines anderen Zeichens) (if(>(strlen str)0) (progn (setq cset(str-list cset)) (setq len(strlen str)) (while(member(substr str len 1)cset) (setq len(1- len)) ) (substr str 1 len) ) ) ) ;| Anwendungsbeispiele: (str-ltrimset " __String" " _") => "String" Mit dieser Grundausstattung sind wir nun in der Lage, das Repertoir sehr schnell zu vervollständigen. Um eine Funktion zu implementieren, die white space entfernt, benötigen wir folgendes Set von Einzelzeichen: 9,10,13,32. Die Funktionen für den Anfang und das Ende könnten so aussehen: |; (defun str-ltrim(str / ) ; entfernt "Whitespaces" vom Anfang der Zeichenkette (str-ltrimset str (str-untok (mapcar'chr '(9 10 13 32)) "")) ) (defun str-rtrim(str / ) ; entfernt "Whitespaces" vom Ende der Zeichenkette (str-rtrimset str (str-untok (mapcar'chr '(9 10 13 32)) "")) ) ;| Anwendungsbeispiele: (str-ltrim " (4 5 6) ") => "(4 5 6) " (str-ltrim " (4 5 6) ") => " (4 5 6)" Ausgesprochen schlicht fällt dann unsere allgemeine Trim-Funktion aus, die sowohl vorne als auch hinten abschneidet. Und wenn wir schon dabei sind, machen wir gleich noch eine Alternative, die (bei Zahlen) evtl. auftretende führende Nullen mit entfernt: entfernt white space an beiden Enden |; (defun str-trim(str / ) ; entfernt white space an beiden Enden (str-ltrim (str-rtrim str)) ) ; entfernt white space an beiden Enden sowie führende Nullen (defun str-trimz(str / ) ; entfernt white space an beiden Enden sowie führende Nullen (str-ltrimset (str-rtrim str) (mapcar'chr'(9 10 13 32 48)) ) ) ;| Anwendungsbeispiele: (str-trim " (4 5 6) ") => "(4 5 6)" (str-trimz " 00004.75\n") => "4.75" Nun aber zum umgekehrten Fall: Zeichenketten müssen manchmal auch mit einem Zeichen aufgefüllt werden. Hier haben wir es immer nur mit einem einzigen Füllzeichen zu tun, Sets sind ausgeschlossen. Allerdings muss jetzt eine Länge angegeben werden, auf die die Zeichenkette gebracht werden soll. Die erste Funktion füllt links auf, die zweite rechts, und die dritte zentriert den String zwischen den Füllzeichen. Alle drei Funktionen schneiden den String ab, wenn die gewünschte Länge kürzer ist als die tatsächlich vorhandene. |; (defun str-lpadchar(str nl ch / ) ;füllt links auf mit Zeichen ch bis Länge nl (cond ( (=(strlen str)nl)str) ( (>(strlen str)nl)(substr str 1 nl)) (1(str-lpadchar(strcat ch str)nl ch)) ) ) (defun str-rpadchar(str nl ch / ) ;füllt rechts auf mit Zeichen ch bis Länge nl (cond ( (=(strlen str)nl)str) ( (>(strlen str)nl)(substr str 1 nl)) (1(str-rpadchar(strcat str ch)nl ch)) ) ) (defun str-cpadchar(str nl ch / len) ;füllt auf mit Zeichen ch bis Länge nl Zentriert (setq len(strlen str)) (cond ( (= len nl)str) ( (> len nl)(substr str (/(- len nl)2) nl)) (1 (str-rpadchar (str-lpadchar str(+ len(/(- len nl)2))ch) nl ch ) ) ) ) ;| Anwendungsbeispiele: (str-lpadchar "String" 10 "_") => "____String" (str-rpadchar "String" 10 "_") => "String____" (str-cpadchar "String" 10 "_") => "__String__" Ähnlich wie bei den Trim-Funktionen können wir nun Funktionen definieren, bei denen das Füllzeichen fest verdrahtet ist. Als Beispiele: Leerstellen und Nullen (zum bündigen Darstellen von Zahlen): |; (defun str-lpad(str nl / )(str-lpadchar str nl " ")) ;füllt links mit Leerzeichen (defun str-rpad(str nl / )(str-rpadchar str nl " ")) ;füllt rechts mit Leerzeichen (defun str-cpad(str nl / )(str-cpadchar str nl " ")) ;füllt zentriert mit Leerzeichen (defun str-lpadz(str nl / )(str-lpadchar str nl "0")) ;füllt links mit Nullen (defun str-rpadz(str nl / )(str-rpadchar str nl "0")) ;füllt rechts mit Nullen ;| Und nun noch ein kurzer Absatz zu den bereits erwähnten Prädikatfunktionen (Testfunktionen). Ich stelle hier einige vor, ohne im Einzelnen darauf einzugehen, sie sind einfach zu verstehen. Bei der Benennung halte ich mich an die Scheme-Konvention, Prädikatfunktionen am Ende des Namens mit einem Fragezeichen zu versehen (in AutoLisp wird eher der Buchstabe p verwendet, aber das auch nicht durchgängig). Argument kann ein Zeichen (als String) oder aber die ASCII-Nummer eines Zeichens sein. |; (defun isalnum? (c / ) ; testet ob alphanumerisch (Ascii 48 bis 57, 65 bis 90 oder 97 bis 122) (if(=(type c)'STR)(setq c (ascii c))) (or(> 58 c 47)(> 91 c 64)(> 123 c 96)) ) (defun isalpha?(c / ) ; testet ob Buchstabe (Ascii 65 bis 90 oder 97 bis 122) (if(=(type c)'STR)(setq c(ascii c))) (or(> 91 c 64)(> 123 c 96)) ) (defun isascii?(c / ) ; testet ob einfacher Asciibereich (Ascii kleiner 128) (if(=(type c)'STR)(setq c(ascii c))) (> 128 c 0) ) (defun iscntrl?(c / ) ; testet ob Steuerzeichen (Ascii kleiner 30) (if(=(type c)'STR)(setq c(ascii c))) (> 31 c 0) ) (defun isdigit?(c / ) ; testet ob numerisch (Ascii 48 bis 57) (if(=(type c)'STR)(setq c(ascii c))) (> 58 c 47) ) (defun islower?(c / ) ; testet ob Kleinbuchstabe (Ascii 97 bis 122) (if(=(type c)'STR)(setq c(ascii c))) (> 123 c 96) ) (defun isprint?(c / ) ; testet ob Druckbares Zeichen (Ascii 32 bis 126) (if(=(type c)'STR)(setq c(ascii c))) (> 127 c 31) ) (defun ispunct?(c / ) ; testet ob Interpunktionszeichen !"'(),.:;?[]{} (if(=(type c)'STR)(setq c(ascii c))) (member c '(33 34 39 40 41 44 46 58 59 63 91 93 123 125) ) ) (defun isspace?(c / ) ; testet ob whitespace (Leerzeichen, Zeilenwechsel, Wagenrücklauf, Tab) (if(=(type c)'STR)(setq c(ascii c))) (member c '(9 10 13 32)) ) (defun isupper?(c / ) ; testet ob Großbuchstabe (Ascii 65 bis 90) (if(=(type c)'STR)(setq c(ascii c))) (> 91 c 64) ) ;| Mit dieser Grundausstattung an Funktionen zur Bearbeitung von Zeichenketten ausgestattet, können wir jetzt dem zweiten Kapitel zu diesem Thema gelassen entgegensehen, in dem das Thema 'Zeichenketten' fortgesetzt wird. |; ;***************************************************************************************************************************************************** ;***************************************************************************************************************************************************** ;***************************************************************************************************************************************************** ;***************************************************************************************************************************************************** ;| Im ersten Kapitel über die Bearbeitung von Zeichenketten haben wir uns bereits mit einer Grundausstattung von Funktionen versorgt, mit denen man Zeichenketten am Anfang und am Ende formatieren kann. Außerdem wurde ein Set von Funktionen für das Testen von Einzelzeichen vorgestellt. Hier geht es jetzt weiter mit einem Bündel von neuen Lisp-Funktionen, mit denen man Zeichenketten durchsuchen sowie Teile aus Zeichenketten entfernen bzw. einfügen kann. Unsere erste hier vorgestellte Funktion sucht eine Zeichenkette innerhalb einer anderen. Wird sie gefunden, wird die Position zurückgegeben, anderenfalls ist der Rückgabewert nil. Der Suchstring kann ein einzelnes Zeichen sein, er kann aber auch aus mehreren Zeichen bestehen. Gleich dazu gibt es eine Variante, die nicht zwischen Groß- und Kleinschreibung unterscheidet |; (defun str-pos(str c / i l ls lc) ; sucht den String c in str und gibt die Position zurück, Groß- und Kleinschreibung werden berücksichtigt (setq i 1) (setq ls(strlen str)) (setq lc(strlen c)) (setq l(1+(- ls lc))) (while(and(<= i l)(/=(substr str i lc)c)) (setq i(1+ i)) ) (if(<= i l)i) ) ;Diese Funktin stammt von Stefan Wickel (defun str-lastpos(str c / i l ls lc) ; sucht den String c in str und gibt die letzte Position zurück, Groß- und Kleinschreibung werden berücksichtigt (setq ls(strlen str)) (setq lc(strlen c)) (setq l(1+ (- ls lc))) (setq i l) ;stzt i auf den letzten möglichen Beginn von c (while(and(> i 0)(/=(substr str i lc)c)) (setq i(1- i)) ) (if(> i 0)i) ) (defun str-ipos(str c / ) ; sucht den String c in str und gibt die Position zurück, Groß- und Kleinschreibung werden ignoriert (str-pos(strcase str)(strcase c)) ) ;Diese Funktin stammt von Stefan Wickel (defun str-lastipos(str c / ) ; sucht den String c in str und gibt die letzte Position zurück, Groß- und Kleinschreibung werden ignoriert (str-lastpos(strcase str)(strcase c)) ) ;| Anwendungsbeispiele: (str-pos "c:\\root" "\\") => 3 (str-ipos "Tintenfisch" "Fisch") => 7 Die nächste Variante arbeitet wieder mit einem Set von Zeichen anstelle eines Suchstrings, wie wir das bereits aus dem vorigen Kapitel über Strings kennen: |; (defun str-posset(str cset / i) ; sucht ein Zeichen des Strings cset in str (setq i 1) (while (and (<= i(strlen str)) (not(str-pos cset(substr str i 1))) ) (setq i(1+ i)) ) (if(> i(strlen str))nil i) ) ;| Anwendungsbeispiel: (str-posset "Hier wurde geändert" "ÄÖÜäöüß") => 13 Die nachfolgende Funktion fügt eine Zeichenkette an der angegebenen Position in einen anderen String ein: |; (defun str-insert(str neu pos / ) ;fügt eine Zeichenkette an der angegebenen Position in einen anderen String ein (strcat (substr str 1(1- pos)) neu (substr str pos) ) ) ;| Anwendungsbeispiel: (str-insert "Dies ist ein Text" "anderer " 14) => "Dies ist ein anderer Text" Und die Funktion tut in etwa das Gegenteil: Sie entfernt an der angegebenen Position eine bestimmte Menge von Zeichen: |; (defun str-remove(str pos n / ) ;entfernt an der angegebenen Position eine bestimmte Menge von Zeichen (strcat (substr str 1(1- pos)) (substr str(+ pos n)) ) ) ;| Anwendungsbeispiel: (str-remove "Eisenbahn" 4 2) => "Eisbahn" Ähnlich wie (subst ...) bei den Listen ersetzt die folgende Funktion einen Teilstring innerhalb eines Strings durch einen anderen String. Natürlich kann es sich hier um einzelne Zeichen oder auch Folgen aus Zeichen handeln: |; (defun str-subst(str neu alt / i la str2) ;ersetzt die folgende Funktion einen Teilstring innerhalb eines Strings durch einen anderen String (setq i 1) (setq la(strlen alt)) (setq str2 "") (while(<= i(strlen str)) (if (=(substr str i la)alt) (progn (setq str2(strcat str2 neu)) (setq i(+ i la)) ) (progn (setq str2(strcat str2(substr str i 1))) (setq i(1+ i)) ) ) ) str2 ) ;| Anwendungsbeispiele: (str-subst "c:/dir/subdir/file.ext" "\\" "/") => "c:\\dir\\subdir\\file.ext" Die nächste Funktion zählt, wie oft das Token tok im String str vorkommt. Die zweite Version arbeitet wieder unabhängig von der Groß- und Kleinschreibung: |; (defun str-count(str tok / res) ;zählt, wie oft das Token tok im String str vorkommt (setq res 0) (while(>(strlen str)0) (if(=(str-pos str tok)1) (progn (setq res(1+ res)) (setq str(substr str(1+(strlen tok)))) ) (progn (setq str(substr str 2)) ) ) ) res ) (defun str-icount(str tok / ) ;zählt, wie oft das Token tok im String str vorkommt unabhängig von der Groß- und Kleinschreibung (str-count(strcase str)(strcase tok)) ) ;| Anwendungsbeispiele: (str-count "Abakadabra" "a") => 4 (str-icount "Abakadabra" "a") => 5 Im dritten Kapitel zum Thema Strings werden wir uns dann mit dem Zerlegen von Strings anhand von Trennzeichen befassen. Natürlich kommt auch das Gegenteil nicht zu kurz, nämlich das Zusammenfügen von Strings. |; ;*********************************************************************************************************************************************** ;*********************************************************************************************************************************************** ;*********************************************************************************************************************************************** ;*********************************************************************************************************************************************** ;| Die beiden bisherigen Kapitel über Funktionen für die Bearbeitung von Zeichenketten haben uns bereits mit einem soliden Grundstock an Funktionen versorgt. In diesem dritten Kapitel sollen nun noch die Funktionen zum Aufteilen von Strings in eine Liste sowie das Gegenteil, nämlich das Zusammenfügen mit Trennzeichen, vorgestellt werden. Oft haben wir es, um ein Beispiel für das Auftrennen von Zeichenketten zu nennen, mit Datenzeilen aus einer Datei zu tun, bei der die einzelnen Daten-Atome durch ein Trennzeichen voneinander getrennt sind. Eine solche Datenzeile könnte beipielsweise so aussehen: Schraube;M12;1,5;36;367.4;252.6;0.0 Wir brauchen also eine Funktion, die diese Zeichenkette in eine Liste von kleineren Strings zerlegt, wobei das Trennzeichen (hier das Semikolon) verschwindet, da es ja kein Bestandteil der eigentlichen Daten ist. Bevor wir aber diese Funktion selbst definieren, befassen wir uns erst wieder mit einem Vorläufer, der aber auch für sich genommen nützlich ist: |; (defun str-div(str c / i l ) ;teilt den String str an der Stelle in zwei Teile, an dem das Zeichen c erstmals auftritt (setq i 1) (setq l(strlen str)) (while(and(<= i l)(/=(substr str i 1)c)) (setq i(1+ i)) ) (list(substr str 1(1- i))(substr str(1+ i))) ) ;| (str-div ...) teilt den String str an der Stelle in zwei Teile, an dem das Zeichen c erstmals auftritt. Weitere Vorkommen des Zeichens werden ignoriert. Die beiden Teilstrings werden als Liste zurückgegeben. Das gefundene Zeichen c ist in keinem der Teilstrings mehr enthalten (es sei denn, es kommt mehr als einmal vor). Wird das Zeichen gar nicht gefunden, ist das zweite Listenelement ein Leerstring. Die folgende Variante (str-divset ...) sucht nicht nur nach einem Zeichen, sondern überprüft, ob eines der Zeichen aus dem Argument cset gefunden wird. Ansonsten ist die Arbeitsweise gleich: |; (defun str-divset(str cset / i l) ;teilt den String str an der Stelle in zwei Teile, an dem ein Zeichen aus cset erstmals auftritt (setq i 1) (setq l(strlen str)) (while(and(<= i l)(not(str-pos cset(substr str i 1)))) (setq i(1+ i)) ) (list(substr str 1(1- i))(substr str(1+ i))) ) ;| Ein paar Anwendungsbeispiele, die die Wirkung der Funktionen verdeutlichen: (str-div "Schraube;M12;1,5;36;367.4;252.6;0.0" ";") => ("Schraube" "M12;1,5;36;367.4;252.6;0.0") (str-div "Text mit\nZeilenumbruch" "\n") => ("Text mit" "Zeilenumbruch") (str-divset "Text mit\nZeilenumbruch" " \n") => ("Text" "mit\nZeilenumbruch") Man sieht schon: Was jetzt noch fehlt, sind die Funktionen, die das Zerlegen weiterführen. Die beiden nachfolgenden Funktionen bauen auf die beiden vorigen auf und arbeiten rekursiv. Ich denke, daß man bei Zeichenketten ruhig auf Rekursion setzen kann, da in Autolisp immer zeilenweise gelesen wird. Systemgrenzen werden hier also sicherlich nicht erreicht. Sollten Bedenken wegen der Rekursion bestehen, dann sollte man das als Anreiz auffassen, über eine iterative Version nachzudenken. In meiner fast AutoLisp-Praxis bin ich mit dieser Version von (str-tok ...) jedenfalls nie an Grenzen gestoßen, und sie ist in meiner Bibliothek sicherlich die am häufigsten verwendete Funktion für die Bearbeitung von Strings. |; (defun str-tok(str c / tmp) ;teilt den String str an den Stellen, an denen das Zeichen c auftritt (auch mehrfach) (if(/= str "") (progn (setq tmp(str-div str c)) (append(list(car tmp))(str-tok(cadr tmp)c)) ) ) ) (defun str-tokset(str cset / tmp) ;teilt den String str an den Stellen, an denen ein Zeichen aus cset auftritt (auch mehrfach) (if(/= str"") (progn (setq tmp(str-divset str cset)) (append(list(car tmp))(str-tokset(cadr tmp)cset)) ) ) ) ;| Anwendungsbeispiele: (str-tok "Schraube;M12;1,5;36;367.4;252.6;0.0" ";") => ("Schraube" "M12" "1,5" "36" "367.4" "252.6" "0.0") (str-tokset filename "\\/.") => ("C:" "Programme" "AutoCAD" "Support" "acad" "lsp") Das letzte Beispiel zeigt, wie die Trennung mittels eines Sets von Zeichen sinnvoll eingesetzt werden kann, wenn gar nicht so recht klar ist, welches Trennzeichen überhaupt vorliegt. In AutoLisp kann für Dateiangaben sowohl der Backslash "\\" (der natürlich doppelt geschrieben werden muß, da ein einzelner Backslash als Steuerzeichen aufgefasst würde!!!) als auch der normale Schrägstrich verwendet werden. Wieso überhaupt der Backslash als Trennzeichen Einzug in die MS-DOS-Welt und damit auch in Windows halten konnte, wird ausserhalb von Redmond sowieso niemand begreifen. Im Zuge der Internetprogrammierung usw. werden wir aber auch auf Windows-Rechnern immer damit leben müssen, dass Pfade mal den Backslash und mal den Slash enthalten. Zum Glück ist der Slash '/' als Zeichen in Windows-Dateinamen verboten, sonst wäre das Chaos komplett! Da dies aber so ist, können wir mit (str-tokset ... "\\/") beide Fälle gleichzeitig erschlagen. Zum Schluß soll nun noch das Gegenteil der Zerlegung vorgestellt werden: Eine Liste mit Teilstrings soll unter Einfügung eines Trenners zu einer Zeichenkette vereinigt werden: |; (defun str-untok (ls c /) ;fügt eine Liste ls voll Strings zu einem String mit Trennzeichen c zusammen (strcat (car ls) (apply 'strcat (mapcar '(lambda (s / )(strcat c s)) (cdr ls)) ) ) ) ;| Die Anwendungsbeispiel sind jetzt nicht ausgesprochen originell, sondern sachdienlich: (str-untok '("Schraube" "M12" "1,5" "36" "367.4" "252.6" "0.0") ";" ) => "Schraube;M12;1,5;36;367.4;252.6;0.0" ";" (str-untok '("C:" "Programme" "AutoCAD" "Support" "acad" "lsp") "/" ) => "C:/Programme/AutoCAD/Support/acad/lsp") _OOPS!!! - Natürlich ist da jetzt ein Fehler drin! Es kann zu den Funktionen, die mit einem Set von Zeichen arbeiten, keine Gegenteilsfunktion geben. Die Wirkungsweise von (str-untok ...) wird aber trotzdem sehr schön verdeutlicht. Wir alle kennen doch sicher den Unterschied zwischen formalen Fehlern (das sind die, die der Interpreter, Compiler usw. anmeckert) und den inhaltlichen Fehlern (das sind die, die bestenfalls der Kunde anmeckert). Ein solcher inhaltlicher Fehler liegt hier vor: natürlich muss das am Ende acad.lsp heissen! Oder doch nicht??? Das kann doch gar nicht beurteilt werden: Jeder kann sich unter .../support/ ein Unterverzeichnis acad einrichten, und darin wiederum eines namens lsp. Ob die Variable 'filename' wirklich einen Dateinamen enthält oder ein Verzeichnis, das wissen wir doch gar nicht. Wir haben hier einfach den Fall eines möglichen inhaltlichen Fehlers, der aber unserer hier besprochenen Sache überhaupt keinen Abbruch tut. |; ;Diese Funktion stammt von Stefan Wickel (defun str-div-pos (str poslist / strlist von pos) ;teilt den String str an den in poslist angegebenen Stellen und gibt eine Liste mit Strings zurück (setq strlist ()) ;Stringiste auf nil setzen (setq von 1) ;für ersten Durchlauf (foreach pos poslist ;Schleife über alle Trennstellen (if (<= pos (strlen str)) (setq strlist (cons (substr str von (- pos von)) strlist)) ;erzeugt den Teilstring und fügt ihn in die Liste ein (setq strlist (cons "" strlist)) ) (setq von pos) ;neuer Teilungspunkt ) ;End foreach (setq strlist (reverse strlist)) ;dreht die List richtig rum ) ;End str-div-pos (princ)