如何在 Common Lisp 中迭代目录?

发布于 2024-08-04 12:34:05 字数 261 浏览 5 评论 0原文

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

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

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

有什么指点吗?

I'm using OpenMCL on Darwin, and I'd like to do something like:

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

But I can't get directory to return anything other than NIL, and I can't seem to find any good explanation online (other than "its different for each system").

Any pointers?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

心头的小情儿 2024-08-11 12:34:05

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

使用字符串

字符串显然取决于平台:例如 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")

上面应该返回 /Users/foo/Lisp/ 及其所有子目录中的所有 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 路径名语法。

There are basically two ways to specify pathnames:

Using strings

Strings are obviously depending on the platform: Unix syntax vs. Windows syntax for example.

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

You can create a pathname object from a string:

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

The #p above assures that a pathname object (and not a string) is created, when you read it back.

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

So, internally Common Lisp works with pathname objects, but it lets you use normal strings and makes pathname objects from them if needed.

When Common Lisp sees a pathname that has not all components specified (for example the directory is missing), then it fills in the components from the pathname object that is the value of the variable *DEFAULT-PATHNAME-DEFAULTS* .

With the function DESCRIBE you can look at the components of a pathname (here 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

Using the Lisp functions creating pathname objects

MAKE-PATHNAME is the function and it takes a few keyword arguments to specify the components.

Sometimes it is also useful to create a new pathname based on an existing one:

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

If you use the function DIRECTORY it is useful to use a pathname with wildcards. DIRECTORY then will return a list of matching pathnames. The name DIRECTORY is slightly misleading, since DIRECTORY does not list the contents of a directory, but lists the matching pathnames for (usually) a pathname with wildcards. The wildcards can match a sequences of characters in components like /foo/s*c/list*.l*. There is also the wild card ** , which is used to match parts of a directory hierarchy like /foo/**/test.lisp , which matches all files test.lisp under the directory foo and its subdirectories.

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

Above should return a list of all lisp files in /Users/foo/Lisp/ and all its subdirectories.

To return the .c files in a single directory use:

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

Note that DIRECTORY returns a list of pathname objects (not a list of strings).

? (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 uses a pathname object that is created by MAKE-PATHNAME. It returns all the files that match /Lisp/cl-http/cl-http-342/server/md5.* .

This is the same as:

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

which is shorter, but depends on the Unix pathname syntax.

无所的.畏惧 2024-08-11 12:34:05

您的路径名规范是否包含通配符? Common Lisp 的路径名一开始有点难以掌握 - 至少对我来说是......作为 CLHSdirectory 函数上声明:

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

为了让你的路径名包含通配符,你可以尝试 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 功能可能更容易使用比普通的标准目录功能。

Does your pathname specification contain a wildcard? Common Lisp's pathname stuff is somewhat hard to grasp at first - at least for me it was... As the CLHS states on the directory function:

If the pathspec is not wild, the
resulting list will contain either
zero or one elements.

In order to have your pathname include a wildcard, you might try the make-pathname function, like

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

Or even

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

I found the CL-FAD library a great help for dealing with pathnames and the file system. In particular, its list-directory function might be easier to use than the plain standard directory function.

ゃ懵逼小萝莉 2024-08-11 12:34:05

实现目录列表的现代 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 以外的平台上几乎无法使用,恕我直言)。

The modern Common Lisp library implementing directory listing is IOLIB.

It works like this:

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

Note that no trailing slash or wildcards are required. It is very robust and can even process file names with incorrectly encoded unicode characters.

Differences compared to CL-FAD:

  • The objects you get are IOLIB file paths, a replacement for CL's pathnames which is closer what the underlying OS does.
  • IOLIB implements its routines using CFFI, so it works the same on all Lisp implementations (provided IOLIB has a backend for the operating system), in contrast to CL-FAD, which tries to abstract over the implementation's DIRECTORY function with all its quirks.
  • In contrast to CL-FAD, iolib deals correctly with symlinks (one major issue with CL-FAD that makes it virtually unusable on platforms other than Windows IMHO).
宛菡 2024-08-11 12:34:05

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

编辑:也可使用uiop:directory-files。 str:包含?可以通过搜索来完成。

;; 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")

如果正确使用通配符,它​​肯定可以得到改进。不过,这是您现在可以使用的代码片段:)

参考资料:

I'll add an example that works for me, for the sake of a code snippet. I use osicat (similar to cl-fad) and str.

edit: also with uiop:directory-files. str:contains? could be done with search.

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

returns

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

It can certainly be improved my a proper use of wildcards. However that's a snippet you can use right now : )

References:

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文