In addition to using streams to write to and read from the REPL, we can also use streams to write to and read from files.
You can create a file stream in Common Lisp in several ways. The best way is to use the with-open-file
command. As you’ll see shortly, this command contains special bug-prevention features that make it safer to use than other available file commands. The following example uses with-open-file
to write the string "my data"
to a file named data.txt
:
>(with-open-file (
my-stream "data.txt" :direction :output)
(print "my data" my-stream))
In this example, the with-open-file
command binds the output stream to the name my-stream
. This causes a file output stream to be created with the name my-stream
. This stream will be available within the body of the with-open-file
command (until the final closing bracket ), and any data we send to this stream will end up in the file named data.txt
on the disk. The print
command references my-stream
as the destination for its output . Therefore, after running this example, you should find a new file named data.txt
in the folder from which you launched CLISP. This file has the text "my data
" as its content.
Specifying :output
as the direction for with-open-file
creates an output stream. To make this an input stream instead, we could change the direction to :input
, as follows:
>(with-open-file (my-stream "data.txt" :direction :input)
(read my-stream))
"my data"
As you can see, this causes the data—the same data written to the file in the previous example—to be read in from the file.
As you learned in Chapter 6, the print
and read
commands can print and read any of the basic Common Lisp data types. This functionality makes it easy to use streams to store data from your programs to the hard drive. Here is a more complicated example that writes an association list (alist) to a file:
>(let ((animal-noises '((dog . woof)
(cat . meow))))
(with-open-file (my-stream "animal-noises.txt" :direction :output)
(print animal-noises my-stream)))
((DOG . WOOF) (CAT . MEOW)) >(with-open-file (my-stream "animal-noises.txt" :direction :input)
(read my-stream))
((DOG . WOOF) (CAT . MEOW))
In this example, we’re creating an association table of animals and the sounds they make. We create a new alist named animal-noises
. We put keys for dog
and cat
into this list. Now we can write this alist to a new file called animal-noises.txt
. Later, we can easily reconstitute this alist from the file .
The with-open-file
command can take keyword parameters that modify its behavior. For instance, you can tell the command what to do if a file with the given name already exists. In the following example, we’ll display an error message using the :if-exists
keyword parameter:
>(with-open-file (my-stream "data.txt" :direction :output :if-exists :error)
(print "my data" my-stream))
*** - OPEN: file #P"/home/user/data.txt" already exists
Alternatively, you may simply want the existing file to be overwritten. In that case, set the :if-exists
keyword parameter to :supersede
, as follows:
>(with-open-file (my-stream "data.txt" :direction :output
:if-exists :supersede)
(print "my data" my-stream))
"my data"
The with-open-file
command gives you a very succinct way to work with files. Unlike most programming languages, when using this command, you don’t need to open and close files manually, and you don’t need to worry about potentially messing up your files by failing to properly close them. (Actually, Common Lisp has lower-level commands for opening and closing files as well, but with-open-file
packages them in a clean way that hides all the ugly details.)
The main purpose of with-open-file
is to acquire a file resource. It takes command of the file and assumes the responsibility of closing it. In fact, even if the code inside the with-open-file
throws an ugly error that stops the program dead, with-open-file
will still close the file properly to make sure this resource stays intact.
Common Lisp has many commands that begin with with-
that will safely allocate resources in this way. These with-
commands, available in the core Lisp libraries, are built with Lisp’s awesome macro system. You’ll learn more about Lisp macros, and how to create your own with-
commands, in Chapter 16.
3.15.3.167