Closing Files
As anyone who has written code that deals with lots of files knows, it’s important to close files when you’re done with them, because file handles tend to be a scarce resource. If you open files and don’t close them, you’ll soon discover you can’t open any more files.5 It might seem straightforward enough to just be sure every **OPEN**
has a matching **CLOSE**
. For instance, you could always structure your file using code like this:
(let ((stream (open "/some/file/name.txt")))
;; do stuff with stream
(close stream))
However, this approach suffers from two problems. One is simply that it’s error prone—if you forget the **CLOSE**
, the code will leak a file handle every time it runs. The other—and more significant—problem is that there’s no guarantee you’ll get to the **CLOSE**
. For instance, if the code prior to the **CLOSE**
contains a **RETURN**
or **RETURN-FROM**
, you could leave the **LET**
without closing the stream. Or, as you’ll see in Chapter 19, if any of the code before the **CLOSE**
signals an error, control may jump out of the **LET**
to an error handler and never come back to close the stream.
Common Lisp provides a general solution to the problem of how to ensure that certain code always runs: the special operator **UNWIND-PROTECT**
, which I’ll discuss in Chapter 20. However, because the pattern of opening a file, doing something with the resulting stream, and then closing the stream is so common, Common Lisp provides a macro, **WITH-OPEN-FILE**
, built on top of **UNWIND-PROTECT**
, to encapsulate this pattern. This is the basic form:
(with-open-file (stream-var open-argument*)
body-form*)
The forms in body-forms are evaluated with stream-var bound to a file stream opened by a call to **OPEN**
with open-arguments as its arguments. **WITH-OPEN-FILE**
then ensures the stream in stream-var is closed before the **WITH-OPEN-FILE**
form returns. Thus, you can write this to read a line from a file:
(with-open-file (stream "/some/file/name.txt")
(format t "~a~%" (read-line stream)))
To create a new file, you can write something like this:
(with-open-file (stream "/some/file/name.txt" :direction :output)
(format stream "Some text."))
You’ll probably use **WITH-OPEN-FILE**
for 90-99 percent of the file I/O you do—the only time you need to use raw **OPEN**
and **CLOSE**
calls is if you need to open a file in a function and keep the stream around after the function returns. In that case, you must take care to eventually close the stream yourself, or you’ll leak file descriptors and may eventually end up unable to open any more files.