Reading Files in Lisp

(defun check (e)
  (when e
    (error e)))

(defun main ()
  ;; Perhaps the most basic file reading task is
  ;; slurping a file's entire contents into memory.
  (let ((dat (with-open-file (stream "/tmp/dat" :direction :input)
               (let ((contents (make-string (file-length stream))))
                 (read-sequence contents stream)
                 contents))))
    (format t "~A" dat))

  ;; You'll often want more control over how and what
  ;; parts of a file are read. For these tasks, start
  ;; by opening a file to obtain a stream.
  (with-open-file (f "/tmp/dat" :direction :input)
    ;; Read some bytes from the beginning of the file.
    ;; Allow up to 5 to be read but also note how many
    ;; actually were read.
    (let ((b1 (make-string 5)))
      (let ((n1 (read-sequence b1 f :end 5)))
        (format t "~D bytes: ~A~%" n1 (subseq b1 0 n1))))

    ;; You can also seek to a known location in the file
    ;; and read from there.
    (file-position f 6)
    (let ((b2 (make-string 2)))
      (let ((n2 (read-sequence b2 f :end 2)))
        (format t "~D bytes @ ~D: ~A~%" n2 6 (subseq b2 0 n2))))

    ;; Other methods of seeking are relative to the
    ;; current cursor position,
    (file-position f :current)

    ;; and relative to the end of the file.
    (file-position f (- (file-length f) 10))

    ;; The Common Lisp standard provides functions that may
    ;; be helpful for file reading. For example, reads
    ;; like the ones above can be more robustly
    ;; implemented with read-sequence.
    (file-position f 6)
    (let ((b3 (make-string 2)))
      (let ((n3 (read-sequence b3 f :end 2)))
        (format t "~D bytes @ ~D: ~A~%" n3 6 b3)))

    ;; There is no built-in rewind, but
    ;; (file-position f 0) accomplishes this.
    (file-position f 0)

    ;; Common Lisp doesn't have a direct equivalent to bufio,
    ;; but you can implement buffered reading using
    ;; with-input-from-string and read-line
    (let ((content (make-string (file-length f))))
      (read-sequence content f)
      (with-input-from-string (s content)
        (let ((line (read-line s)))
          (format t "5 bytes: ~A~%" (subseq line 0 5)))))))

(main)

To run this program:

$ echo "hello" > /tmp/dat
$ echo "lisp" >> /tmp/dat
$ sbcl --script reading-files.lisp
hello
lisp
5 bytes: hello
2 bytes @ 6: li
2 bytes @ 6: li
5 bytes: hello

This Lisp code demonstrates various file reading operations, including reading entire files, seeking to specific positions, and reading portions of files. It uses Common Lisp’s file I/O functions to achieve similar functionality to the original Go code.

Note that Lisp doesn’t have a direct equivalent to Go’s bufio package, but we can achieve similar functionality using Lisp’s built-in string manipulation and I/O functions.

Next, we’ll look at writing files.