open
(open filename &optional direction element-type)
Opens a file and returns a stream. The optional direction is :input (the default -- open for reading), :output (create or truncate, open for writing), :io or :probe. :io opens ONE bidirectional stream: reads, writes and file-position share a single cursor, so after writing, (file-position s :start) and a read answer what was just written; like :output it creates or truncates by default and reads the whole :if-exists table. :probe answers a file stream that is already CLOSED -- the program may ask typep or pathname about it but not read it -- or nil when the file is not there. The keyword-argument shape (open filename :direction :output :if-exists :append) opens for writing WITHOUT truncating, so every write lands at the end of an existing file. The whole :if-exists table is read on an OUTPUT open: :supersede (the default here) and the version spellings :new-version/:rename/:rename-and-delete all write over the old content, :error signals a file-error, nil answers nil instead of opening, and :overwrite opens for writing at position 0 WITHOUT truncating, so whatever the writes do not cover survives. On an input or probe open :if-exists is not consulted at all, whatever it says. :if-does-not-exist is :create, :error or nil, defaulting -- as CL prescribes -- to :create for a superseding output or :io open, nil for :probe and :error everywhere else, INCLUDING an appending or overwriting one (so :if-exists :append on a file that is not there signals unless :if-does-not-exist :create says otherwise). On an :io stream :append only STARTS the cursor at the end; a later file-position moves it for writes too. :external-format accepts :utf-8/:default, the one format any backend writes. The optional element type selects the stream kind: 'character (the default) opens a text stream for read/read-line/write-line, and an integer type opens a binary stream for read-byte/write-byte/read-sequence/write-sequence. The widths are SBCL's: a type needing up to 8, 16, 32 or 64 bits moves 1, 2, 4 or 8 octets per element, a wider one ceil(bits/8) -- so '(unsigned-byte 8), 'unsigned-byte, 'bit and '(integer 0 200) are all one octet, '(unsigned-byte 12) two, '(signed-byte 33) eight. Nothing is packed below an octet and nothing is biased: an element is its value as little-endian octets, two's complement when the type admits negatives. stream-element-type answers the widened type, and file-length / file-position count elements. On the compiled backends only 'character and the '(unsigned-byte 8) spellings may be COMPUTED (below); any other integer type must be written literally there. In the keyword shape an option value may be COMPUTED ((open path :direction dir :element-type type)): it is read when the call runs and dispatched onto the matching literal shape, which is what lets a portable wrapper take its options as arguments. A literal value is resolved at compile time exactly as before, and a value outside the supported set signals an error when the call runs. The returned stream is a self-describing VALUE -- streamp and (typep s 'file-stream) answer t for it -- wrapping a backend handle (an index into a stream table on the interpreter/JVM, the WASI file descriptor on WASM); it is valid only within the producing run, and should be passed to the matching read/write functions and then close. On WASM the path is resolved against the preopened directories -- a relative path against the first one, an absolute path against the preopened directory whose name is its longest prefix -- so run with --dir. Prefer with-open-file, which closes the stream automatically.
(let ((s (open "data.txt")))
(print (read-line s))
(close s))
This opens data.txt for input, reads its first line, and closes the stream. Passing :output instead would create or truncate the file for writing; (open "data.bin" :input '(unsigned-byte 8)) opens the same kind of handle in binary mode.
A file that cannot be opened signals a file-error on every backend, reporting OPEN: cannot open file <name>; file-error-pathname answers the designator that was passed.