Lesson 12
Files

A computer's memory cannot hold information when the power is turned off.  Thus, information to be saved must be stored in a file on disk. Advanced AutoLISP programs often need to save information between sessions, or access stores of information.  This requires the ability to work with files.

AutoLISP has functions which can find, read, and create files.  Generally, these files will be "plain ASCII text files," the same sort of files created with an editor like Windows NotePad.

Files and memory buffers

When AutoLISP creates a file, two things are accomplished.  First, an empty file is created on disk and given a name determined by the user, or, in some cases, by the program.  Second, a buffer is created in memory for temporary storage of information until it is sent to the file on disk.

It would be inefficient to write every little piece of information directly to disk because it takes much longer to write information to disk than to memory.  So, as each piece of information is generated by the program, it is sent to the buffer.  When the buffer becomes full, its contents are flushed to disk automatically by the operating system.  If the program finishes generating information and sending it to the buffer and the buffer is only partly full, the operating system flushes the buffer's contents to disk only if the program tells it to do so.

The situation is somewhat similar when AutoLISP needs to read information from a file.  It would be inefficient to read every piece of information directly from disk.  So, instead, a whole section of information is read from the file and placed in a buffer so the program can read individual pieces of information quickly from the buffer.  When additional information is needed from the file, the next section is automatically read into the buffer by the operating system, replacing the first section.

So AutoLISP does not write to, or read from, the disk directly.  Rather, it writes to, and reads from, the buffer.  The operating system takes care of flushing the buffer to disk and refilling the buffer from disk.

The three step procedure

Using files always involves these three programming steps:

All three steps should be carried out whether the file is being written to or read from.  Don't forget to close the file.  It is obvious that closing the file is crucial when you are writing to a file -- if you don't, information in the buffer will be lost.  Although it may not be so obvious, closing the file is also crucial when you are reading from a file -- if you don't, the operating system may not allow you to edit or delete the file later.

Here is a very simple example which opens a file by the name of FILE1.TXT, then write the phrase "Line one" to the buffer, then closes the file (which actually places the information in the file on disk).

    (setq fw (open "FILE1.TXT" "w"))
    (write-line "Line one" fw)
    (close fw)

The open function has a file name as its first argument, and a single lower case letter as its second argument:
  - "w" to open the file for writing
  - "a" to open the file for appending (places new information at the end of the file)
  - "r" to open the file for reading
The open function returns the memory location of the buffer, which in the above example is stored in variable fw.

The phrase "Line one" is written to the buffer by the write-line function, and is placed on disk when the buffer is closed by the close function.

If you get a directory listing after executing the first line, you would see that a new file has been created with the name FILE1.TXT, but it is empty.  If you get a directory listing after the second line, the file is still empty.

When you open a file for reading, the information that is placed in the buffer is not removed from the file.  In fact, the information in a file that is opened for reading is not altered in any way.

Various situations can be encountered when opening a file for writing or appending.  For example, if you open a file for writing, but a file by that name already exists, that existing file will be destroyed because it will be overwritten by the new information going into the new file.  Or, suppose you open a file for appending, but that file does not exist.  Then the file is opened and the information is placed at the beginning of the file, just as though you had opened it for writing.

Practice session

The following exercise can be done best if you have two windows open at the same time: an AutoCAD window and a DOS window.  In the DOS window you should change to the folder that AutoCAD uses by default.  (That is,  if you right click on your AutoCAD shortcut, select Properties, and select the Shortcut tab, this folder is identified as the "Start in" folder.)  This exercise will be easiest if this default folder is empty, which may require that you create a new folder and temporarily establish it as your default AutoCAD folder.

If you are not comfortable using the DOS commands suggested below, you can substitute the following Windows procedures:  In place of the DOS DIR command, use the Windows Explorer to get file listings.  To view the exact size of the file, right click the file and selecting properties.  In place of the DOS TYPE command, double click the file and open it with Notepad.

At the AutoCAD command prompt enter:
(setq fw (open "test1" "w"))
In DOS:
Use DIR to list the files.  TEST1 should appear in the listing, with 0 bytes.
At the AutoCAD command prompt enter:
(write-line "Line one" fw)
In DOS:
Use DIR to list the files.  TEST1 should appear in the listing, still with 0 bytes because "Line one" is still in the buffer and has not been sent to disk.
At the AutoCAD command prompt enter:
(write-line "Line two" fw)
In DOS:
(same as above)
At the AutoCAD command prompt enter:
(close fw)
In DOS:
Use DIR to list the files.  TEST1 should now show 20 bytes.  The text strings are only 16 bytes long.  The extra bytes appear because DOS places two characters at the end of each line: new line (ascii 10) and carriage return (ascii 13).  These characters can be seen by running DEBUG TEST1 and examining the two hex numbers (0A and 0D respectively) at the end of each string.
Experiment in a similar manner, opening the same file for appending.
Experiment in a similar manner, opening the same file for writing and using read-line instead of write-line.

The findfile and getfiled functions

You should look up both the findfile function and the getfiled function in the "15 - Files" in the "Catalog of function definitions."  The findfile function is illustrated below in the sample program called balloon2 (section 15, added line 4).  The getfiled function is illustrated below in the sample program called read (section 20, first line).
 


 Sample program 1, write

This program creates a file containing any number of lines.  The while loop (section 20) allows the user to enter any number of lines.  Every time the user enters a line, it is written to the file (buffer).  When the user finally hits 'Enter' to quit entering lines, the getstring functions returns a null string (""), which causes the test in the while loop to fail.

The byte counting algorithm in the while loop adds an extra two bytes each time a line is written to file.  This is done to account for the two extra characters (ascii 10 and 13) which DOS places at the end of each line.

------ marker ------ beginning of working program ------ try it out ------

;|  WRITE
    Copyright © 1988,1998 Ronald W. Leigh
    Input: File name and several lines
    Output: Creates file and places lines in it
Variables:
bytes  Number of bytes in file
fname  File name
fw     File pointer, file opened for writing
lin    Text line
lnum   Line number
vvv    Temporary value in foreach loop        |;

;10====PROGRAM, GET FILE NAME, OPEN FILE

(defun c:write (/ bytes fname fw lin lnum vvv)
(setq fname (getstring "\nFile name: "))
(setq fw (open fname "w"))

;20====GET LINES, SEND TO FILE

(setq lnum 1 bytes 0)
(while (/= "" (setq lin (getstring 1
      (strcat "\nLine " (itoa lnum) " for file ('Enter' to quit): "))))
  (write-line lin fw)
  (setq lnum (1+ lnum))
  (setq bytes (+ bytes (strlen lin) 2)))
(close fw)

;30====REPORT

(foreach vvv
  (list "\n(File contains " (1- lnum) " lines, " bytes " bytes.")
  (princ vvv))
(princ)
)

-------- marker -------- end of working program -------- try it out --------

 Sample program 2, read

This program reads a text file with any number of lines, placing the lines in a list.  The lines can then be displayed on the text screen or placed in the drawing.  This program illustrates opening a file for reading (see section 20, third line).

Since the getfiled function is used (section 20, first line), it is unlikely that the user could select a file that did not exist since the dialog box displays a message in such a case, then returns to the dialog box.  However, it could easily happen that the user would hit 'Escape', which causes the getfiled function to return nil.  This makes the rest of the program meaningless, so the variable fname is used as a flag for the rest of section 20 as well as for sections 30 and 40.  Because each of these sections begins with an if statement using fname is the test, the remainder of the if statement (which extends to the end of the section) will not execute unless fname is non-nil.  (By the way, the quit and exit functions are often used to accomplish the same thing.  However, they trigger an error and thus work best if an error routine is in place, so they are not used here.)

------ marker ------ beginning of working program ------ try it out ------

;|  READ
    Copyright © 1988,1998 Ronald W. Leigh
    Input: Name of file
    Output: Displays file on text screen or places in drawing
Variables:
ans     Answer to yes-no options
cnt     Count
fname   File name
fr      File pointer, opened for reading
lin     Line of text
llist   Line list
lspac   Line spacing
obm     Old blipmode
oom     Old osnap mode
sp      Start point of text
temp    Temporary value in request for line spacing
thei    Text height
ts      Current text style
tshei   Text style height
vvv     Temporary value in foreach function            |;

;10====PROGRAM, SETUP

(defun c:read (/ ans cnt fname fr lin llist lspac
                 obm oom sp temp thei ts tshei vvv)
(setq pi 3.1415926535897932385)
(setq obm (getvar "blipmode") oom (getvar "osmode"))

;20====GET FILE NAME, READ FILE INTO LIST, DISPLAY NUMBER OF LINES

(setq fname (getfiled "Select a Text File" "" "" 0))
(if fname (progn
  (setq fr (open fname "r"))
  (setq llist () cnt 0)
  (while (setq lin (read-line fr))
    (setq llist (append llist (list lin)))
    (setq cnt (1+ cnt)))
  (close fr)
  (princ "\n**File contains ")
  (princ cnt)
  (princ " lines.")
))

;30====OPTION--DISPLAY FILE ON TEXT SCREEN

(if fname (progn
  (initget "Yes No")
  (setq ans (getkword "\nDisplay file on text screen? (Y/N): "))
  (if (= ans "Yes") (progn
    (textscr) (terpri) (terpri)
    (foreach vvv llist (princ vvv) (terpri))
    (terpri)))
))

;40====OPTION--ADD TEXT LINES TO DRAWING

(if fname (progn
  (initget "Yes No")
  (setq ans (getkword "\nAdd text lines to drawing? (Y/N): "))
  (if (= ans "Yes") (progn
    (graphscr)
    (initget 1)
    (setq sp (getpoint "\nStartpoint: "))
    (setq ts (getvar "textstyle"))
    (setq tshei (cdr (assoc 40 (tblsearch "style" ts))))
    (if (> tshei 0)
      (progn
        (princ (strcat "\n**Current text style has fixed height of " (rtos tshei 2 3)))
        (setq thei tshei))
      (progn
        (initget 7)
        (setq thei (getdist sp "\nText height: "))))
    (setq lspac (* 2 thei))
    (initget 6)
    (setq temp (getdist sp (strcat "\nLine spacing <" (rtos lspac 2 3) ">: ")))
    (if temp (setq lspac temp))
    (setvar "cmdecho" 0)
    (foreach vvv llist
      (if (> tshei 0)
        (command ".text" sp 0 vvv)
        (command ".text" sp thei 0 vvv))
      (setq sp (polar sp (* pi 1.5) lspac)))
    (setvar "cmdecho" 1)
  ))
))

;50====RESTORE

(setvar "blipmode" obm)
(setvar "osmode" oom)
(princ)
)

-------- marker -------- end of working program -------- try it out --------

 Sample program 3, balloon2

The balloon program was used as a sample program in lesson 6.  In this lesson, however, we have added 16 lines so the balloon program will save its default balloon size in a file.  The added lines are marked in the program below.  (By the way, this program works properly only if the balloon2 program is in its own file, namely BALLOON2.LSP, rather than combined with other programs in a file with a different name.)

Many firms use the same size balloon on all their assemblies.  Without the added 16 lines, the user would have to enter that balloon size every time a new drawing is started (or, if system variable LISPINIT is set to 0, every time AutoCAD is started).  But with the added lines, the user enters the balloon size only once.  It is saved in a file and retrieved every time balloon2 is run after that.  The user can reset the balloon size at any time, and the new setting is saved in the file.

Added lines 3 - 6:  These lines (section 15) find the location of the BALLOON2.LSP file and place it in variable blndfl.  This location is used so that the file which saves the user's balloon diameter (BALLOON2.DIA) will reside in the same folder as the program file.  Variable blndfl is used later by the section which reads the balloon diameter from the file (added lines 7-12) and the section which writes a new diameter to file when it is changed by the user (added lines 13-16).  The first if statement checks to make sure variable blndfl is not a string before trying to find the program file.  If it is a string (presumably the location of the program file) there is no need to search for the program file.  The second if statement is needed just in case the program file is not found and the findfile function returns nil, which would cause the strlen and substr functions to crash (added line 6).

Added lines 7 - 12:  Before attempting to read the balloon diameter from the BALLOON2.DIA file, the if statement checks to make sure of three things: (1) that variable blndia is null, (2) that blndfl is a string, and (3) that file BALLOON2.DIA can be found.  If these three tests pass, BALLOON2.DIA is read and the string is converted to a real before being placed in blndia.  When the program is used again in the same drawing, variable blndia will already be set so there is no need to open the file repeatedly -- only when the diameter is changed and the new diameter is written to file in lines 13-16.

Added lines 13 - 16:  The default balloon diameter is shown in the prompt (just above added line 13).  If the user simply hits 'Enter', variable temp is nil and the test in the if statement (just above added line 13) fails.  However, if the user enters a value, and if variable blndfl is a string, then the new value is saved in the BALLOON2.DIA file.

------ marker ------ beginning of working program ------ try it out ------

;|  BALLOON2 -- Stores balloon diameter in file
    Copyright © 1988,1998 Ronald W. Leigh
    Input: size of balloon, arrow location,
           balloon location, size of balloon, number
    Output: draws assembly balloon
Variables:
blndfl  Balloon diameter file location, GLOBAL                 ADDED  1
blndia  Balloon diameter, GLOBAL
blnnum  Balloon number, GLOBAL
cen     Center of balloon
ep      Endpoint of arrow
fr/fw   File descriptors: file opened for reading/writing      ADDED  2
oom     Old osnap mode
sp      Start point of arrow
temp    Temp. value for blndia and blnnum input
ts      Name of current text style
tshei   Text style height                              |;

;10====MAIN PROGRAM, SETUP

(defun c:balloon2 (/ cen ep fr fw oom sp temp ts tshei)
(setq oom (getvar "osmode"))
(graphscr)

;15====GET LOCATION OF BALLOON2.LSP PROGRAM FILE

(if (/= (type blndfl) 'STR) (progn                            ;ADDED  3
  (setq blndfl (findfile "balloon2.lsp"))                     ;ADDED  4
  (if (= (type blndfl) 'STR)                                  ;ADDED  5
    (setq blndfl (substr blndfl 1 (- (strlen blndfl) 12)))))) ;ADDED  6

;20====GET SIZE, START POINT, CENTER, & NUMBER

(if (and (null blndia)                                        ;ADDED  7
         (= (type blndfl) 'STR)                               ;ADDED  8
         (findfile (strcat blndfl "balloon2.dia"))) (progn    ;ADDED  9
  (setq fr (open (strcat blndfl "balloon2.dia") "r"))         ;ADDED 10
  (setq blndia (atof (read-line fr)))                         ;ADDED 11
  (close fr)))                                                ;ADDED 12
(if (null blndia)
  (if (= 0 (getvar "dimscale"))
    (setq blndia (/ (getvar "dimtxt") 0.28))
    (setq blndia (/ (* (getvar "dimtxt") (getvar "dimscale")) 0.28))
  )
)
(initget 6)
(setq temp (getreal (strcat "\nBalloon diameter <" (rtos blndia 2 3) ">: ")))
(if temp (progn
  (setq blndia temp)
  (if (= (type blndfl) 'STR) (progn                           ;ADDED 13
    (setq fw (open (strcat blndfl "balloon2.dia") "w"))       ;ADDED 14
    (write-line (rtos blndia 2 4) fw)                         ;ADDED 15
    (close fw)))))                                            ;ADDED 16

(setvar "osmode" 512)
(initget 1)
(setq sp (getpoint "\nStartpoint of arrow: "))
(setvar "osmode" 0)

(initget 1)
(setq cen (getpoint sp "\nLocation of balloon: "))

(if (null blnnum) (setq blnnum 0))
(setq blnnum (1+ blnnum))
(initget 6)
(setq temp (getint (strcat "\nBalloon number <" (itoa blnnum) ">: ")))
(if temp (setq blnnum temp))

;30====CALC. END POINT OF ARROW, GET TEXT STYLE HEIGHT, DRAW BALLOON

(setq ep (polar cen (angle cen sp) (/ blndia 2)))

(setq ts (getvar "textstyle"))
(setq tshei (cdr (assoc 40 (tblsearch "style" ts))))
(if (> tshei 0)
  (princ "\nText style has fixed height; may not fit balloon."))

(setvar "cmdecho" 0)
(command ".dim1" "leader" sp ep) (command)
(command ".circle" cen "D" blndia)

(if (> tshei 0)
  (command ".text" "M" cen 0 blnnum)
  (command ".text" "M" cen (* 0.28 blndia) 0 blnnum)
)

;40====RESET

(setvar "cmdecho" 1)
(setvar "osmode" oom)
(princ)
)

-------- marker -------- end of working program -------- try it out --------
 


HOME


Copyright © 1988, 1998 Ronald W. Leigh