Skip to content
Pathnames and Files
step 1/4

Reading — step 1 of 4

Learn

~2 min readPackages, Sequences, Pathnames

Common Lisp has a portable filesystem abstraction — pathnames — that work across Unix, Windows, even old VMS. The flip side: it's verbose for everyday use.

Pathnames

(make-pathname :directory '(:absolute "home" "user")
               :name "data"
               :type "txt")
;; → #P"/home/user/data.txt"

Fields: host, device, directory, name, type, version.

Parse from a string:

(pathname "/home/user/data.txt")

File operations

(probe-file "data.txt")              ; pathname if exists, nil if not
(file-write-date "data.txt")          ; universal time
(file-length stream)                  ; size when opened
(delete-file "data.txt")
(rename-file "old.txt" "new.txt")

Listing a directory

Not in standard Common Lisp! Use directory:

(directory "*.lisp")                  ; list of pathnames
(directory "src/**/*.lisp")           ; recursive

Or use cl-fad (file-and-directory) or uiop:directory-files for portability:

(uiop:directory-files "src/")

Reading a file

(with-open-file (in "data.txt" :direction :input)
    (loop for line = (read-line in nil :eof)
          until (eq line :eof)
          do (format t "~a~%" line)))

Writing a file

(with-open-file (out "output.txt"
                     :direction :output
                     :if-exists :supersede)
    (format out "line 1~%")
    (format out "line 2~%"))

Slurping

(defun slurp (path)
    (with-open-file (in path)
        (with-output-to-string (out)
            (loop for line = (read-line in nil nil)
                  while line
                  do (write-line line out)))))

The uiop:read-file-string (UIOP is bundled with ASDF) is a one-liner:

(uiop:read-file-string "data.txt")

Common patterns

Append to a log file:

(with-open-file (log "app.log"
                     :direction :output
                     :if-exists :append
                     :if-does-not-exist :create)
    (format log "~a: ~a~%"
            (get-universal-time)
            message))

Read a CSV-like file:

(with-open-file (in "data.csv")
    (loop for line = (read-line in nil :eof)
          until (eq line :eof)
          collect (split-string line #\,)))

Beyond standard CL

For real-world file work, almost everyone uses:

  • uiop — Universal Imperative Operations Pack (ASDF-bundled)
  • cl-fad — file and directory utilities
  • local-time — date/time handling (CL's built-in is awkward)
(uiop:read-file-lines "data.txt")
(uiop:directory-files "src/" "*.lisp")
(uiop:run-program '("git" "status") :output :string)

These are de facto standards for non-trivial code.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…