Nyquist / XLISP 2.0  -  Contents | Tutorials | Examples | Reference

File I/O


  1. File I/O Examples - from the XLISP 2.0 manual, by David Betz
  2. Files and Directories
  3. Testing Existence
  4. Text File I/O
  5. Binary File I/O

1  File I/O Examples


1.1  Input from a File


The basics of file i/o with XLISP:

The open function takes a single required argument which is the name of the file to be opened. This name can be in the form of a string or a symbol. The open function returns an object of type 'file-stream' if it succeeds in opening the specified file. It returns the value NIL if it fails.

In order to manipulate the file, it is necessary to save the value returned by the open function. This is usually done by assigning it to a variable with the setq special form or by binding it using let or let*. Here is an example:

(setq file-stream (open "init.lsp" :direction :input))

Evaluating this expression will result in the file 'init.lsp' being opened. The file object that will be returned by the open function will be assigned to the variable 'file-stream'.

It is now possible to use the file for input. To read an expression from the file, just supply the value of the 'file-stream' variable as the optional 'stream' argument to the read function:

(read file-stream)

Evaluating this expression will result in reading the first expression from the file 'init.lsp'. The expression will be returned as the result of the read function. More expressions can be read from the file using further calls to the read function. When there are no more expressions to read, the read function will return NIL [or whatever value was supplied as the second argument to read].

Once you are done reading from the file, you should close it. To close the file, use the close function:

(close file-stream)

Evaluating this expression will cause the file to be closed.

  Back to Top


1.2  Output to a File


Writing to a file is pretty much the same as reading from one. You need to open the file first. This time you should use the open function to indicate that you will do output to the file:

(setq file-stream (open "test.dat" :direction :output))

Evaluating this expression will open the file 'test.dat' for output. If the file already exists, its current contents will be discarded. If it doesn't already exist, it will be created. In any case, a 'file-stream' object will be returned by the open function. This file object will be assigned to the 'file-stream' variable.

It is now possible to write to this file by supplying the value of the 'file-stream' variable as the optional 'stream' parameter in the print function.

(print "Hello there" file-stream)

Evaluating this expression will result in the string "Hello there" being written to the file "test.dat". More data can be written to the file using the same technique.

Once you are done writing to the file, you should close it. Closing an output file is just like closing an input file:

(close file-stream)

Evaluating this expression will close the output file and make it permanent.

  Back to Top


1.3  A Slightly More Complicated File Example


This example shows how to open a file, read each Lisp expression from the file and print it. It demonstrates the use of files and the use of the optional 'stream' argument to the read function.

(do* ((file-stream (open "test.dat" :direction :input))
      (expression (read file-stream) (read file-stream)))
     ((null expression) nil)
  (print expression))

  Back to top


1.4  Closing Files via 'unwind-protect'


To make sure that the file gets closed in the end, the file i/o functions can be wrapped by an unwind-protect form:

(let ((file-stream (open "test.dat" :direction :input)))
  (unwind-protect
    ;; protect-form
    (do ((expression (read file-stream) (read file-stream)))
        ((null expression) nil)
      (print expression))
    ;; clean-up form
    (when file-stream (close file-stream))))

This pattern can be found in many file i/o functions:

(let ((file-stream (open filename :direction direction)))
  (unwind-protect
    (progn
      ;; do something with the file-stream
      )
    (when file-stream (close file-stream))))

  Back to top


3  Testing Existence


Note that these function are meant to prevent accidents during basic file i/o, they may not be able to test if a file or directory physically exists or is a link to a different place.

The Nyquist listdir function can be used to test if a directory exists:

(defun directory-exists-p (string)
  (and (listdir string) t))

Testing if a file exists is a bit more tricky because on Unix [including Linux and Mac OS X] a directory is a special kind of file, so the XLISP open function also can open directories. That's why we first must make sure that the filename string is not the name of an existing directory:

(defun file-exists-p (string)
  (or (stringp string) (error "not a string" string))
  (unless (listdir string)
    (let ((file-stream (open string)))
      (when file-stream
        (close file-stream)
        string))))

Before creating a new file it's always a good idea to check if a file or directory with the same name already exists:

(defun file-or-directory-exists-p (string)
  (or (stringp string) (error "not a string" string))
  (when (or (listdir string)
            (let ((file-stream (open string)))
              (when file-stream
                (close file-stream)
                t)))
    string))

  Back to Top


Nyquist / XLISP 2.0  -  Contents | Tutorials | Examples | Reference