23.2 Opening and Closing Files

When a file is opened, a stream object is constructed to serve as the file system’s ambassador to the Lisp environment; operations on the stream are reflected by operations on the file in the file system. The act of closing the file (actually, the stream) ends the association; the transaction with the file system is terminated, and input/output may no longer be performed on the stream. The stream function close may be used to close a file; the functions described below may be used to open them. The basic operation is open, but with-open-file is usually more convenient for most applications.

[Function] open filename &key :direction :element-type :if-exists :if-does-not-exist :external-format

X3J13 voted in June 1989 to add to the function open a new keyword argument :external-format. This argument did not appear in the preceding argument description in the first edition.

This returns a stream that is connected to the file specified by filename. The filename is the name of the file to be opened; it may be a string, a pathname, or a stream. (If the filename is a stream, then it is not closed first or otherwise affected; it is used merely to provide a file name for the opening of a new stream.)

X3J13 voted in January 1989 to specify that the result of open, if it is a stream, is always a stream of type file-stream.

X3J13 voted in March 1988 to specify exactly which streams may be used as pathnames. See section 23.1.6.

X3J13 voted in January 1989 to specify that open is unaffected by whether the first argument, if a stream, is open or closed. If the first argument is a stream, open behaves as if the function pathname were applied to the stream and the resulting pathname used instead.

X3J13 voted in June 1989 to clarify that open accepts only non-wild pathnames; an error is signaled if wild-pathname-p would be true of filename.

X3J13 voted in June 1989 to require open to accept logical pathnames (see section 23.1.5).

The keyword arguments specify what kind of stream to produce and how to handle errors:

__________________________________________________________________________

Заметка для реализации: The various file systems in existence today have widely differing capabilities. A given implementation may not be able to support all of these options in exactly the manner stated. An implementation is required to recognize all of these option keywords and to try to do something “reasonable” in the context of the host operating system. Implementors are encouraged to approximate the semantics specified here as closely as possible.

As an example, suppose that a file system does not support distinct file versions and does not distinguish the notions of deletion and expunging (in some file systems file deletion is reversible until an expunge operation is performed). Then :new-version might be treated the same as :rename or :supersede, and :rename-and-delete might be treated the same as :supersede.

If it is utterly impossible for an implementation to handle some option in a manner close to what is specified here, it may simply signal an error. The opening of files is an area where complete portability is too much to hope for; the intent here is simply to make things as portable as possible by providing specific names for a range of commonly supportable options.

__________________________________________________________________________

X3J13 voted in June 1989 to add to the function open a new keyword argument :external-format.

  • :external-format
  • This argument specifies an implementation-recognized scheme for representing characters in files. The default value is :default and is implementation-defined but must support the base characters. An error is signaled if the implementation does recognize the specified format.

    This argument may be specified if the :direction argument is :input, :output, or :io. It is an error to write a character to the resulting stream that cannot be represented by the specified file format. (However, the #\Newline character cannot produce such an error; implementations must provide appropriate line division behavior for all character streams.)

    See stream-external-format.

When the caller is finished with the stream, it should close the file by using the close function. The with-open-file form does this automatically, and so is preferred for most purposes. open should be used only when the control structure of the program necessitates opening and closing of a file in some way more complex than provided by with-open-file. It is suggested that any program that uses open directly should use the special operator unwind-protect to close the file if an abnormal exit occurs.


[Макрос] with-open-file (stream filename {options}*){declaration}* {form}*

with-open-file evaluates the forms of the body (an implicit progn) with the variable stream bound to a stream that reads or writes the file named by the value of filename. The options are evaluated and are used as keyword arguments to the function open.

When control leaves the body, either normally or abnormally (such as by use of throw), the file is automatically closed. If a new output file is being written, and control leaves abnormally, the file is aborted and the file system is left, so far as possible, as if the file had never been opened. Because with-open-file always closes the file, even when an error exit is taken, it is preferred over open for most applications.

filename is the name of the file to be opened; it may be a string, a pathname, or a stream.

X3J13 voted in March 1988 to specify exactly which streams may be used as pathnames. See section 23.1.6.

X3J13 voted in June 1989 to clarify that with-open-file accepts only non-wild pathnames; an error is signaled if wild-pathname-p would be true of the filename argument.

X3J13 voted in June 1989 to require with-open-file to accept logical pathnames (see section 23.1.5).

For example:

(with-open-file (ifile name
                 :direction :input)
  (with-open-file (ofile (merge-pathname-defaults ifile
                                                  nil
                                                  "out")
                         :direction :output
                         :if-exists :supersede)
    (transduce-file ifile ofile)))

X3J13 voted in June 1989 to specify that the variable stream is not always bound to a stream; rather it is bound to whatever would be returned by a call to open. For example, if the options include :if-does-not-exist nil, stream will be bound to nil if the file does not exist. In this case the value of stream should be tested within the body of the with-open-file form before it is used as a stream. For example:

(with-open-file (ifile name
                 :direction :input
                 :if-does-not-exist nil)
  ;; Process the file only if it actually exists.
  (when (streamp name)
    (compile-cobol-program ifile)))


__________________________________________________________________________

Заметка для реализации: While with-open-file tries to automatically close the stream on exit from the construct, for robustness it is helpful if the garbage collector can detect discarded streams and automatically close them.

__________________________________________________________________________