如何在 Common Lisp 中迭代目录?

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

我在 Darwin 上使用 OpenMCL,我想做一些类似的事情:

(loop for f in (directory "somedir")
  collect (some-per-file-processing f))

但是我无法让

directory
返回除
NIL
以外的任何内容,而且我似乎在网上找不到任何好的解释(除了“每个系统都有所不同”)。

有什么指点吗?

lisp filesystems directory common-lisp
4个回答
32
投票

基本上有两种指定路径名的方法:

使用字符串

字符串显然取决于平台:例如 Unix 语法与 Windows 语法。

"/Users/foo/bar.text"  is a valid pathname
"/Users/foo/*/foo.*"   is a valid pathname with two wildcards

您可以从字符串创建路径名对象:

? (pathname "/Users/bar/foo.text")
#P"/Users/bar/foo.text"

上面的

#p
确保当您读回它时,会创建一个路径名对象(而不是字符串)。

? #P"/Users/bar/foo.text"
#P"/Users/bar/foo.text"

因此,Common Lisp 在内部使用路径名对象,但它允许您使用普通字符串并根据需要从它们创建路径名对象。

当 Common Lisp 发现路径名未指定所有组件(例如缺少目录)时,它会从路径名对象中填充组件,该对象是变量

*DEFAULT-PATHNAME-DEFAULTS*
的值。

使用 DESCRIBE 函数,您可以查看路径名的组成部分(此处为 Clozure CL):

? (describe (pathname "/Users/bar/*.text"))
#P"/Users/bar/*.text"
Type: PATHNAME
Class: #<BUILT-IN-CLASS PATHNAME>
TYPE: (PATHNAME . #<CCL::CLASS-WRAPPER PATHNAME #x3000401D03BD>)
%PATHNAME-DIRECTORY: (:ABSOLUTE "Users" "bar")
%PATHNAME-NAME: :WILD
%PATHNAME-TYPE: "text"
%PHYSICAL-PATHNAME-VERSION: :NEWEST
%PHYSICAL-PATHNAME-DEVICE: NIL

使用 Lisp 函数创建路径名对象

MAKE-PATHNAME
是函数,它需要一些关键字参数来指定组件。

有时,基于现有路径名创建新路径名也很有用:

(make-pathname :name "foo" :defaults (pathname "/Users/bar/baz.text"))

如果您使用函数

DIRECTORY
,则使用带有通配符的路径名会很有用。
DIRECTORY
然后将返回匹配路径名的列表。名称
DIRECTORY
略有误导性,因为
DIRECTORY
不列出目录的内容,而是列出(通常)带有通配符的路径名的匹配路径名。通配符可以匹配
/foo/s*c/list*.l*
等组件中的字符序列。还有通配符
**
,用于匹配目录层次结构的一部分,例如
/foo/**/test.lisp
,它匹配目录
test.lisp
及其子目录下的所有文件
foo

(directory "/Users/foo/Lisp/**/*.lisp")

上面应该返回

lisp
及其所有子目录中所有
/Users/foo/Lisp/
文件的列表。

要返回单个目录中的

.c
文件,请使用:

(directory "/Users/foo/c/src/*.c")

请注意,

DIRECTORY
返回pathname对象的列表(不是字符串列表)。

? (directory
    (make-pathname
      :name "md5"
      :type :wild
      :directory '(:absolute "Lisp" "cl-http" "cl-http-342" "server")))
(#P"/Lisp/cl-http/cl-http-342/server/md5.lisp"
 #P"/Lisp/cl-http/cl-http-342/server/md5.xfasl")

上面使用了由

MAKE-PATHNAME
创建的路径名对象。它返回所有匹配
/Lisp/cl-http/cl-http-342/server/md5.*
.

的文件

这与:

相同
(directory "/Lisp/cl-http/cl-http-342/server/md5.*")

它更短,但取决于 Unix 路径名语法。


23
投票

您的路径名规范是否包含通配符? Common Lisp 的路径名内容一开始有点难以掌握 - 至少对我来说是这样......正如 directory 函数上的

CLHS
所示:

如果路径规范不是通配符,则 结果列表将包含 零个或一个元素。

为了让您的路径名包含通配符,您可以尝试 make-pathname 函数,例如

(directory (make-pathname :directory '(:absolute "srv" "hunchentoot") :name :wild :type "lisp"))

甚至

(directory (make-pathname :directory '(:absolute "srv" "hunchentoot") :name :wild :type :wild))

我发现 CL-FAD 库对于处理路径名和文件系统有很大帮助。特别是,它的

list-directory
函数可能比普通标准
directory
函数更容易使用。


8
投票

实现目录列表的现代 Common Lisp 库是 IOLIB

它的工作原理如下:

CL-USER> (iolib.os:list-directory "/etc/apt")
(#/p/"trusted.gpg~" #/p/"secring.gpg" #/p/"trustdb.gpg" #/p/"sources.list"
 #/p/"sources.list~" #/p/"apt-file.conf" #/p/"apt.conf.d" #/p/"trusted.gpg"
 #/p/"sources.list.d")

请注意,不需要尾部斜杠或通配符。它非常强大,甚至可以处理带有错误编码的 unicode 字符的文件名。

与 CL-FAD 的差异:

  • 您获得的对象是 IOLIB 文件路径,它是 CL 路径名的替代品,它更接近底层操作系统的功能。
  • IOLIB 使用 CFFI 实现其例程,因此它在所有 Lisp 实现上的工作方式相同(假设 IOLIB 有操作系统的后端),这与 CL-FAD 形成鲜明对比,CL-FAD 试图抽象实现的 DIRECTORY 函数及其所有怪癖。
  • 与 CL-FAD 相比,iolib 可以正确处理符号链接(CL-FAD 的一个主要问题是它在 Windows 以外的平台上几乎无法使用,恕我直言)。

3
投票

为了代码片段,我将添加一个适合我的示例。我使用 osicat (类似于 cl-fad)和 str

编辑:也与

uiop:directory-files
一起使用。 str:包含?可以用
search
来完成。

;; searching for "ref".
(setf *data-directory* "~/books/lisp")
(remove-if-not (lambda (it)
                   (str:contains? "ref" (namestring it)))
                (osicat:list-directory *data-directory*))

退货

(#P"~/books/lisp/common-lisp-quick-reference-clqr-a4-booklet-all.pdf"
 #P"~/books/lisp/common-lisp-quick-reference-clqr-a4-consec.pdf"
 #P"~/books/lisp/commonLisp-interactive-approach-reference-buffalo.pdf")

通过正确使用通配符当然可以改进它。不过,这是您现在可以使用的片段:)

参考资料:

© www.soinside.com 2019 - 2024. All rights reserved.