如何遍历Common Lisp中的目录?

时间:2009-09-10 06:50:47

标签: lisp filesystems directory common-lisp

我在达尔文上使用OpenMCL,我想做类似的事情:

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

但我无法让directory返回NIL以外的任何内容,而且我似乎无法在网上找到任何好的解释(除了“每个系统的不同之处”)。< / p>

任何指针?

4 个答案:

答案 0 :(得分:27)

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

  • 使用字符串

字符串显然取决于平台:例如,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看到没有指定所有组件的路径名(例如缺少目录)时,它会填充路径名对象中的组件,该组件是variabel * DEFAULT-PATHNAME-DEFAULTS *的值。 / p>

使用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,它匹配目录foo及其子目录下的所有文件test.lisp。

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

上面应该返回'/ Users / foo / Lisp /'及其所有子目录中所有'lisp'文件的列表。

要在单个目录中返回.c文件,请使用:

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

请注意,DIRECTORY返回路径名对象列表(不是字符串列表)。

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

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

这与:

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

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

答案 1 :(得分:18)

您的路径名规范是否包含通配符? Common Lisp的路径名一开始有点难以理解 - 至少对我来说这是......作为directory函数的CLHS状态:

  

如果pathspec不是狂野的,那么   结果列表将包含   零或一个元素。

为了让您的路径名包含通配符,您可以尝试使用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函数更容易使用。

答案 2 :(得分: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试图用实现的DIRECTORY函数抽象它怪异。
  • 与CL-FAD相反,iolib正确处理符号链接(CL-FAD的一个主要问题,使其在Windows IMHO以外的平台上几乎无法使用)。

答案 3 :(得分:2)

为了代码片段,我将添加一个适合我的示例。我使用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")

当然可以改善我对通配符的正确使用。不过这是你现在可以使用的片段:)

参考文献: