How can I read the contents of a file into a list in Lisp?

20,811

Solution 1

How about

(defun get-file (filename)
  (with-open-file (stream filename)
    (loop for line = (read-line stream nil)
          while line
          collect line)))

Solution 2

I'll add libraries in.

edit even easier, with uiop, which is included in ASDF:

(uiop:read-file-lines "file.txt")

https://common-lisp.net/project/asdf/uiop.html#UIOP_002fSTREAM

also has

(uiop:read-file-string "file")

With Alexandria's read-file-into-string and split-sequence:

(alexandria:read-file-into-string "file.txt")
(split-sequence:split-sequence #\Newline *)

With str:

(str:lines (str:from-file "file.txt"))

More recipes on files: https://lispcookbook.github.io/cl-cookbook/files.html

Solution 3

Where are the problems?

(defun get-file (filename)
  (let ((x (open filename)))
    (when x
      (loop for line = (read-line x nil)
            while line
            do (list line)))    ; <-- you are not collecting, just doing
    (close x)))                 ; <- the function returns the value of CLOSE

(defun get-file (filename)
  (let ((x (open filename :if-does-not-exist nil))
        (contents (list nil)))
    (when x
      (loop for line = (read-line x nil)
            while line
            do (cons contents line)))  ; <-- again, the cons goes nowhere
    (close x) contents))               ; <-- CONTENTS has never been changed

(defun get-file (filename)
  (let ((x (open filename :if-does-not-exist nil))
        (contents nil))
    (when x
      (loop for line = (read-line x nil)
            while line
            do (append contents line)))  ; <- again the result goes nowhere
    (close x) contents))                 ; <-- CONTENTS has never been changed

LOOP

The DO clause of LOOP will just execute something for side effects.

COLLECT will collect the result and the LOOP then will return a list of collected values upon exit.

Opening and closing of files

As already mentioned, use WITH-OPEN-FILE instead of OPEN/CLOSE. WITH-OPEN-FILE will close the file upon leaving the dynamic scope. Not just from a normal exit, but also upon error conditions, by using UNWIND-PROTECT to ensure the execution.

Reading file contents

If you want to read the contents of a file, you can use the function READ-SEQUENCE. With the usual problems. For example when you read an ASCII file as text into a string, the string may be shorter than the file. For example Common Lisp will represent internally CRLF with a single character, on platforms where CRLF is newline. Another example: in Unicode supporting implementations the UTF-8 code in the file may be replaced with a single character.

Share:
20,811
Sterling
Author by

Sterling

Updated on July 05, 2022

Comments

  • Sterling
    Sterling almost 2 years

    I want to read in the contents of a file into a list. Some of my attempts so far have been -

    (defun get-file (filename)
      (let ((x (open filename)))
        (when x
          (loop for line = (read-line x nil)
         while line do (list line)))
        (close x)))
    
    (defun get-file (filename)
      (let ((x (open filename :if-does-not-exist nil)) (contents (list nil)))
        (when x
          (loop for line = (read-line x nil)
         while line do (cons contents line)))
        (close x) contents))
    
    (defun get-file (filename)
      (let ((x (open filename :if-does-not-exist nil)) (contents nil))
        (when x
          (loop for line = (read-line x nil)
         while line do (append contents line)))
        (close x) contents))
    

    None of these worked. Can anyone tell me a way? Or even better - how to put all of the contents into an array?

  • Sterling
    Sterling over 13 years
    (with-open-file (f filename) Why do you have an f before filename there? what does collecting line do? And how does that collaborate with while line at the end?
  • user1025901
    user1025901 over 13 years
    with-open-file opens the file called filename and associates the stream with f. collecting collects the various values of line in a list, until the while is nil. The Common Lisp HyperSpec and Practical Common Lisp are your friends!
  • user1025901
    user1025901 over 13 years
    In other words, with-open-file does all the work of open and close and the let.
  • Rainer Joswig
    Rainer Joswig over 13 years
    stream is a better name than f. Also it useful to move the WHILE in front of COLLECT.
  • MatthewRock
    MatthewRock almost 5 years
    This should become the accepted answer. Every time I need to do file loading, I end up writing my own utility function. Now I've learned that my compiler has two(one for a list, one for a single string). Thanks!