How can I read the contents of a file into a list in Lisp?
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.
Sterling
Updated on July 05, 2022Comments
-
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 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 over 13 years
with-open-file
opens the file calledfilename
and associates the stream withf
.collecting
collects the various values ofline
in a list, until thewhile
isnil
. The Common Lisp HyperSpec and Practical Common Lisp are your friends! -
user1025901 over 13 yearsIn other words,
with-open-file
does all the work ofopen
andclose
and thelet
. -
Rainer Joswig over 13 yearsstream is a better name than f. Also it useful to move the WHILE in front of COLLECT.
-
MatthewRock almost 5 yearsThis 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!