如何在 Lisp 中将文件内容读入列表?

问题描述 投票:0回答:4

我想将一个文件的内容读入一个列表。到目前为止,我的一些尝试是 -

(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))

这些都不起作用。谁能告诉我一个方法?或者更好——如何将所有内容放入一个数组中?

list file-io lisp common-lisp
4个回答
28
投票

怎么样

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

28
投票

我会添加库。

edit 使用

uiop
更容易,它包含在 ASDF 中:

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

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

还有

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

随着Alexandria的

read-file-into-string
和分裂序列:

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

str

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

有关文件的更多食谱:https://lispcookbook.github.io/cl-cookbook/files.html


18
投票

问题在哪里?

(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

循环

DO
LOOP
子句只会执行一些副作用。

COLLECT
将收集结果,
LOOP
然后将在退出时返回收集值列表。

打开和关闭文件

如前所述,使用

WITH-OPEN-FILE
代替
OPEN
/
CLOSE
WITH-OPEN-FILE
将在离开动态范围时关闭文件。不仅从正常退出,而且在错误情况下,通过使用
UNWIND-PROTECT
来确保执行。

读取文件内容

如果你想读取一个文件的内容,你可以使用函数

READ-SEQUENCE
。与通常的问题。例如,当您将 ASCII 文件作为文本读入字符串时,字符串可能比文件短。例如,在 CRLF 是换行符的平台上,Common Lisp 将在内部用单个字符表示 CRLF。另一个例子:在 Unicode 支持实现中,文件中的 UTF-8 代码可能会被单个字符替换。


0
投票

2023 更新

使用Transducer 模式可以很容易地处理文件的内容(或任何其他数据流)。具体对于一个文件:

(t:transduce #'t:pass #'t:cons #p"path/to/file.txt")

这会将给定文件中的每一行读入列表。请注意,没有太多理由这样做,因为之后您可能想在列表上执行的任何处理都可以在during 转换本身完成,从而避免一次将整个文件拉入内存的需要。

例如统计一个文件的字数:

(t:transduce (t:comp (t:map #'str:words) ;; (2) Split each line into a list of words.
                     #'t:concatenate)    ;; (3) Flatten that list back into a single stream.
             #'t:count       ;; (4) Count the total number of split words.
             #p"foobar.txt") ;; (1) Read from a file, one line at a time.

请注意,任一示例中的

t:
名称空间前缀都是假设在导入时已为库提供的昵称,如:

(:local-nicknames (#:t #:transducers))
© www.soinside.com 2019 - 2024. All rights reserved.