|
AutoCAD... AutoLISP... VisualLISP... |
|
[47] Plik tekstowy - odczyt |
| Gdy czasem znadzie się coś w "starociach"... zaskakującym może być że, owe starocie jeszcze całkiem dobrze działają... ;). Poniższy tekst ukazał się w nieistniejącym już piśmie "Magazyn 3D" (5/1999) w roku 1999!: |
![]() |
| Możliwość dostępu do danych zawartych w plikach zewnętrznych z poziomu AutoLISP-u, ma wielkie znaczenie dla różnego typu programów i aplikacji AutoCAD-a. Trudno sobie wyobrazić jakiekolwiek oprogramowanie do parametryzacji obiektów, bez dostępu do danych zewnętrznych. Przesyłanie danych do edytora graficznego AutoCAD-a, z programów obliczeniowych, tworzących zestawienia itp. często odbywa się poprzez pliki zewnętrzne, z reguły tekstowe. Mimo pewnych ograniczeń (przede wszystkim długi czas dostępu i wielkość plików danych), stosowanie mechanizmów dostępu do plików tekstowych, przez AutoLISP, nierzadko jest jedyną metodą na tworzenie, nawet dużych a wydajnych i elastycznych programów. Za stosowaniem plików tekstowych do wymiany danych z AutoCAD-em przemawiają również takie cechy jak prostota składni, możliwość edycji bez używania specjalnych narzędzi, wygoda i szybkość wprowadzania zmian, możliwość koncentrowania danych w jednym miejscu.
» Odczyt danych z plików tekstowych Wykorzystywanie standardowej funkcji (READ-LINE) posiada następujace ograniczenia:
jk:SYS_READ-FILES. Jest ona tak skonstruowana
aby odczytywać dane z pliku na dwa sposoby:
- odczytanie całego pliku i zwrócenie listy łańcuchów tekstowych (linii pliku)
- odczytanie jednej linii określonej przez jej pozycję
|
;; ----------------------------------------------------------------------------- ; ;; ODCZYT PLIKU TEKSTOWEGO ; ;; (1999) kojacek ; ;; ; ;; Funkcja zwraca listę, łańcuch lub NIL ; ;; ; ;; Argumenty funkcji: ; ;; =================== ; ;; CODE - gdy równe NIL funkcja zwraca listę łańcuchów tekstowych będących ; ;; liniami całego pliku. Gdy CODE różne od NIL (musi być typu INTEGER) ; ;; zwraca łańcuch tekstowy będący linią określoną przez ten argument. ; ;; (Uwaga: dla pierwszej linii CODE=0 dla drugiej 1 itd.) ; ;; Gdy INT jest większe niż ilość linii w pliku funkcja zwraca NIL ; ;; FILE - nazwa pliku - gdy bez scieżki dostępu plik poszukiwany jest na ; ;; standardowej ścieżce poszukiwań AutoCAD-a. Jeśli plik nie znajduje ; ;; się na tej ścieżce trzeba podać nazwę pliku wraz z pełną ścieżką ; ;; dostępu. W obu przypadkach gdy plik nie zostanie znaleziony, ; ;; wyświetlane jest okienko ostrzegawcze i funkcja zwraca NIL ; (defun jk:SYS_Read-Files (CODE FILE / FLN FLD FDAT X) (setq FLN (findfile FILE)) (if FLN (progn (if CODE (progn (setq FLD (open FLN "r")) (repeat CODE (read-line FLD)) (setq FDAT (read-line FLD)) (close FLD) ) (progn (setq FLD (open FLN "r")) (while (setq X (read-line FLD)) (setq FDAT (append FDAT (list X) ) ) ) (close FLD) ) ) ) (alert (strcat "Nie znaleziono pliku " FILE " ")) ) (if FDAT FDAT NIL) ) ;; ----------------------------------------------------------------------------- ; |
Można przetestować teraz funkcję w następujący sposób:
Po załadowaniu tego pliku w linii poleceń wywołanie:(jk:SYS_Read-Files NIL "DANE.DAT")
lub (jk:SYS_Read-Files 3 "DANE.DAT")wyświetli okno ostrzegawcze i zwróci NIL, bo nie ma pliku DANE.DATnatomiast wywołanie: (jk:SYS_Read-Files NIL "ACAD.LIN") zwróci listę wszystkich linii w pliku acad.lin, a wywołanie: (nth 12 (jk:SYS_Read-Files NIL "ACAD.LIN"))prawdopodobnie zwróci: "*CENTER,Center ____ _ ____ _ ____ _ ____ _ ____ _ ____"Inny sposób wybrania (tylko) dwunastej linii z tego pliku wygląda tak: (jk:SYS_Read-Files 12 "ACAD.LIN")zwróci: Z punktu widzenia przykładowego programujacego, ważne jest pobranie tutaj trzynastego wiersza pliku acad.lin, zatem: (jk:SYS_Read-Files 13 "ACAD.LIN")zwróci: "A,1.25,-.25,.25,-.25"Oczywiście zamiast pliku ACAD.LIN można wykorzystać każdy inny plik tekstowy. Funkcja jk:SYS_Read-Files oczywiście spełnia swoje zadanie, kiedy należy odczytać
cały plik, lub odczytać linię na znanej pozycji. W przypadku gdy nie wiemy
w którym miejscu pliku znajdują się dane, a znamy "identyfikator" po którym
linia jest rozpoznawana, wykorzystamy funkcję o nazwie jk:SYS_Read-Lines, której
działanie sprowadza się do:- odczytania jednej, następnej linii po podanym "kluczu", lub - odczytania jednej (tej samej linii) co podany "klucz". Funkcja wygląda tak: |
;; ----------------------------------------------------------------------------- ; ;; ODCZYT LINII PLIKU TEKSTOWEGO ; ;; (1999) kojacek ; ;; ; ;; Funkcja zwraca łańcuch lub NIL ; ;; ; ;; Argumenty funkcji: ; ;; =================== ; ;; CODE - gdy równe NIL funkcja zwraca łańcuch tekstowy będący linią pliku i ; ;; zawierający podany "klucz" (argument KEYN), w przeciwnym wypadku ; ;; (gdy CODE różne od NIL) zwraca następną linię. W przypadku gdy ; ;; KEYN nie istnieje funkcja zwraca NIL. ; ;; Gdy INT jest większe niż ilość linii w pliku funkcja zwraca NIL ; ;; FILE - nazwa pliku - gdy bez scieżki dostępu plik poszukiwany jest na ; ;; standardowej ścieżce poszukiwań AutoCAD-a. Jeśli plik nie znajduje ; ;; się na tej ścieżce trzeba podać nazwę pliku wraz z pełną ścieżką ; ;; dostępu. W obu przypadkach gdy plik nie zostanie znaleziony, ; ;; wyświetlane jest okienko ostrzegawcze i funkcja zwraca NIL ; ;; KEYN - "klucz" poszukiwań. Gdy w pliku występuje więcej odwołań do "klucza" ; ;; zwracane jest zawsze ostatnie napotkane wystąpienie. ; ;; Rozpoznawane są duże i małe litery. ; (defun jk:SYS_Read-Lines (CODE FILE KEYN / FLN FLD FDAT LEN BUF X) (setq LEN (strlen KEYN) FLN (findfile FILE) ) (if FLN (progn (if CODE (progn (setq FLD (open FLN "r")) (while (setq BUF (read-line FLD)) (if (= (substr BUF 1 LEN) KEYN) (setq FDAT BUF) ) ) (close FLD) ) (progn (setq FLD (open FLN "r")) (while (setq BUF (read-line FLD)) (if (= (substr BUF 1 LEN) KEYN) (setq FDAT (read-line FLD)) ) ) (close FLD) ) ) ) (alert (strcat "Nie znaleziono pliku " FILE " ")) ) (if FDAT FDAT NIL) ) ;; ----------------------------------------------------------------------------- ; |
Do testowania można wykorzystać plik acad.lin
Wywołanie:(jk:SYS_Read-Lines T "acad.lin" "*DASHDOT,")zwróci: "*DASHDOT,Dash dot __ . __ . __ . __ . __ . __ . __ . __"czyli linię w której wystąpił klucz, natomiast: (jk:SYS_Read-Lines NIL "acad.lin" "*DASHDOT,")zwraca: "A,.5,-.25,0,-.25"
Warto zaznaczyć że funkcję tę można wykorzystać do czytania plików typu *.INI Na przykład wywołanie: (jk:SYS_Read-Lines T "mtextmap.ini" "isocp.shx=") zwraca: "isocp.shx=ISOCP,0,0,0,2" Ponieważ READ-LINE zawsze zwraca dane jako łańcuch tekstowy osobnym problemem
jest teraz "obróbka" takiego łańcucha otrzymanego przez funkcję jk:SYS_Read-Lines lub
jk:SYS_Read-Fileses. Do tego celu służy przedstawiona poniżej funkcja S_PARSE, której
autorem jest Bill Kramer, została ona opublikowana w miesięczniku CADENCE 4/1997
Funkcję realizującą to samo zadanie (podział łańcucha tekstowego) można również
znaleć w książce "AutoLISP Praktyczny kurs" wydanej przez HELION, której autorem
jest Marek Dudek.
Ponieważ S_PARSE jest krótsza i bardziej prosta (jest bliższa memu sercu bo sam
kiedyś popełniłem bardzo podobną funkcję) przedstawiam ją poniżej:
|
;; ----------------------------------------------------------------------------- ; ;; PODZIAŁ ŁAŃCUCHA TEKSTOWEGO NA LISTĘ ; ;; ; ;; Funkcja rozbija łańcuch tekstowy na listę ; ;; Autor: Bill Kramer - opublikowana w CADENCE 4/97 ; ;; ; ;; Argumenty funkcji: ; ;; =================== ; ;; S - łańcuch tekstowy do podziału ; ;; D - lista separatorów (lista łańcuchów tekstowych) ; ;; ; ;; Uwaga: Jeżeli w łańcuchu do rozbicia znajdują się liczby typu INT, funkcja ; ;; zwraca je jako REAL ; (defun S_PARSE (S D / BUF RES CNT CH DIGS ISNUM) (setq BUF "" CNT 1 ISNUM 'T DIGS '("." "+" "-" "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" ) ) (repeat (strlen S) (setq CH (substr S CNT 1) CNT (1+ CNT) ) (cond ((and (member CH D) (> (strlen BUF) 0) ) (setq RES (cons (if ISNUM (atof BUF) BUF ) RES ) BUF "" ISNUM 'T ) ) ((not (member CH D)) (setq BUF (strcat BUF CH)) (if (null (member CH DIGS)) (setq ISNUM NIL) ) ) ) ) (if (> (strlen BUF) 0) (setq RES (cons (if ISNUM (atof BUF) BUF ) RES ) ) ) (reverse RES) ) ;; ----------------------------------------------------------------------------- ; |
Do testowania tej funkcji można w linii poleceń napisać:(S_PARSE "10.0,.20,tekst,-10" (list ","))
funkcja zwróci: (10.0 0.2 "tekst" -10.0)czyli całkiem piękną listę. Dalej można testować (wykorzystując poprzedni przykład - czyli dane trzynastej linii pliku ACAD.LIN): (S_PARSE (jk:SYS_Read-Files 13 "ACAD.LIN")(list ","))wynikiem będzie lista: ("A" 1.25 -0.25 0.25 -0.25)lub: (S_PARSE (jk:SYS_Read-Lines NIL "acad.lin" "*DASHDOT,")(list ","))zwraca: ("A" 0.5 -0.25 0.0 -0.25)a więc to o co nam chodzi. Odczytanie danych z pliku MTEXTMAP.INI może wyglądać tak: (S_PARSE (jk:SYS_Read-Lines T "mtextmap.ini" "isocp.shx=")(list "," "="))gdzie wynikiem będzie: ("isocp.shx" "ISOCP" 0.0 0.0 0.0 2.0)W książce "AutoLISP czyli programowanie AutoCAD-a" wydanej przez "Helion" której autorami są Joseph Smith i Rusty Gesner podany jest przykład odczytywania danych z pliku typu SDF. Ma on jednak swoje ograniczenia (dane tylko liczbowe i o określonej liczbie znaków). Funkcja S_PARSE nadaje się również do odczytywania
plików SDF, bez względu na typ danych i ich długość, jest zatem bardziej
uniwersalna. Pamiętać należy tylko, iż bez względu na ilość spacji użytych do
rozdzielenia poszczególnych rekordów w pliku, wywołując S_PARSE trzeba jako listę
separatorów podać jedną spację. Oto przykład:(S_PARSE (jk:SYS_Read-Files 51 "SITENAME.TXT")(list " "))zwraca: (431900.0 -770800.0 "Alma-Ata" "Kazachstan")
Wykorzystując przedstawione narzędzia, można pisać bardzo efektywne programy lispowe, czytające dane z plików tekstowych. Oczywiście dla bardziej skompliko- wanych formatów plików tekstowych, narzędzia te mogą okazać się niewystarczające, lecz na ich podstawie można zbudować inne funkcje. Odczyt plików tekstowych, za pomocą AutoLISP-u, to tylko fragment zagadnienia jakim jest dostęp do plików - istnieje jeszcze tworzenie, zapis i aktualizacja plików tekstowych, ale to już osobna opowieść... |
![]()
Tekst: 07-1999. Aktualizacja: 14-11-2009
|
|
|