如何在Common Lisp中搜索带通配符的文件?

Ehv*_*nce 4 wildcard common-lisp pathname

我不满意找到匹配字符串的文件,如下所示:

(remove-if-not (lambda (it)
                   (search "wildcard" (namestring it)))
               (uiop:directory-files "./"))
;; I'll ignore case with str:contains?
;; https://github.com/vindarel/cl-str
Run Code Online (Sandbox Code Playgroud)

如何用unix风格的通配符搜索文件?

如果它不是内置的,我会喜欢uiop的解决方案.也许有Osicatcl-fad(它似乎不是这样,文档经常说"非野性路径名").

如果可以使用双通配符以递归方式遍历目录(./**/*.jpg),则可以获得奖励.

编辑:我已经试过的变种(directory #p"./**/*.jpg"),它返回nil :(也试过#p".*jpg",#p"./.*jpg"...

(wild-pathname-p (pathname "*.jpg"))
(:WILD :WILD-INFERIORS)


(make-pathname :name :wild :type "jpg")
#P"*.jpg"
Run Code Online (Sandbox Code Playgroud)

以下通过jpg扩展获取文件,但它还不是一个合适的通配符:

(directory *)
(#P"/home/vince/cl-cookbook/AppendixA.jpg"
 #P"/home/vince/cl-cookbook/AppendixB.jpg"
 #P"/home/vince/cl-cookbook/AppendixC.jpg")
Run Code Online (Sandbox Code Playgroud)

关于路径名的文档和make-pathname:http://gigamonkeys.com/book/files-and-file-io.html(没有提到通配符)

cor*_*ump 7

SBCL

SBCL支持名称中的通配符.首先,创建一些文件:

(loop 
  with stem = #P"/tmp/stack/_.txt"
  initially (ensure-directories-exist stem)
  for name in '("abc" "def" "cadar" "cdadr" "cddr")
  for path = (make-pathname :name name :defaults stem)
  do (open path :direction :probe :if-does-not-exist :create))
Run Code Online (Sandbox Code Playgroud)

然后,列出包含"a"的所有文件:

CL-USER> (directory #P"/tmp/stack/*a*.txt")
(#P"/tmp/stack/abc.txt" #P"/tmp/stack/cadar.txt" #P"/tmp/stack/cdadr.txt")
Run Code Online (Sandbox Code Playgroud)

pathname包含特定于实现的(有效)名称组件:

CL-USER> (describe #P"/tmp/stack/*a*.txt")
#P"/tmp/stack/*a*.txt"
  [structure-object]

Slots with :INSTANCE allocation:
  HOST       = #<SB-IMPL::UNIX-HOST {10000F3FF3}>
  DEVICE     = NIL
  DIRECTORY  = (:ABSOLUTE "tmp" "stack")
  NAME       = #<SB-IMPL::PATTERN :MULTI-CHAR-WILD "a" :MULTI-CHAR-WILD>
  TYPE       = "txt"
  VERSION    = :NEWEST
; No value
Run Code Online (Sandbox Code Playgroud)

SBCL还定义sb-ext:map-directory了逐个处理文件,而不是首先收集列表中的所有文件.

便携式方案

如果您需要坚持使用标准路径名组件,则可以先directory使用普通通配符调用,然后过滤生成的列表:

CL-USER> (remove-if-not (wildcard "*a*")
                        (directory #P"/tmp/stack/*.txt")
                        :key #'pathname-name)

(#P"/tmp/stack/abc.txt" #P"/tmp/stack/cadar.txt" #P"/tmp/stack/cdadr.txt")
Run Code Online (Sandbox Code Playgroud)

...... wildcard可能基于正则表达式(PPCRE):

(defun parse-wildcard (string)
  (delete ""
          (map 'list
               (lambda (string)
                 (or (cdr (assoc string
                                 '(("*" . :wild)
                                   ("?" . :char))
                                 :test #'string=))
                     string))
               (ppcre:split '(:sequence
                              (:negative-lookbehind #\\)
                              (:register (:alternation #\* #\?)))
                            string
                            :with-registers-p t))
          :test #'string=))
Run Code Online (Sandbox Code Playgroud)

(注意:上面的负面反馈并不能消除逃逸的反斜杠)

(defun wildcard-regex (wildcard)
  `(:sequence
    :start-anchor
    ,@(loop
        for token in wildcard
        collect (case token
                  (:char :everything)
                  (:wild '(:greedy-repetition 0 nil :everything))
                  (t token)))
    :end-anchor))

(defun wildcard (string)
  (let ((scanner (ppcre:create-scanner
                  (wildcard-regex (parse-wildcard string)))))
    (lambda (string)
      (ppcre:scan scanner string))))
Run Code Online (Sandbox Code Playgroud)

中级职能:

CL-USER> (parse-wildcard "*a*a\\*a?\\?a")
(:WILD "a" :WILD "a\\*a" :CHAR "\\?a")

CL-USER> (wildcard-regex (parse-wildcard "*a*a\\*a?\\?a"))
(:SEQUENCE :START-ANCHOR #1=(:GREEDY-REPETITION 0 NIL :EVERYTHING) "a" #1# "a\\*a" :EVERYTHING "\\?a" :END-ANCHOR)
Run Code Online (Sandbox Code Playgroud)


Rai*_*wig 5

没有当前目录,也没有主目录字符

.便携式Common Lisp中不存在表示当前目录的概念。这可能存在于特定的文件系统和特定的实现中。

~表示主目录不存在。在某些实现中,它们可能被视为非便携式扩展。

在路径名字符串中,您具有***作为通配符。这适用于绝对路径名和相对路径名。

默认为默认路径名

Common Lisp具有*default-pathname-defaults*为某些路径名操作提供默认值的功能。

例子

CL-USER 46 > (directory "/bin/*")
(#P"/bin/[" #P"/bin/bash" #P"/bin/cat"   ....   )
Run Code Online (Sandbox Code Playgroud)

现在,在上面已经有点不确定了,或者在Unix上实现了不同:

  • 解决符号链接?
  • 包括“隐藏”文件?
  • 包括类型文件?

下一个:

CL-USER 47 > (directory "/bin/*sh")
(#P"/bin/zsh" #P"/bin/tcsh" #P"/bin/sh" #P"/bin/ksh" #P"/bin/csh" #P"/bin/bash")
Run Code Online (Sandbox Code Playgroud)

使用相对路径名:

CL-USER 48 > (let ((*default-pathname-defaults* (pathname "/bin/")))
               (directory "*sh"))
(#P"/bin/zsh" #P"/bin/tcsh" #P"/bin/sh" #P"/bin/ksh" #P"/bin/csh" #P"/bin/bash")
Run Code Online (Sandbox Code Playgroud)

主目录中的文件:

CL-USER 49 > (let ((*default-pathname-defaults* (user-homedir-pathname)))
              (directory "*"))
Run Code Online (Sandbox Code Playgroud)

相同:

CL-USER 54 > (directory (make-pathname :name "*"
                                       :defaults (user-homedir-pathname)))
Run Code Online (Sandbox Code Playgroud)

查找以下所有以sh结尾的文件/usr/local/

CL-USER 54 > (directory "/usr/local/**/*sh")
Run Code Online (Sandbox Code Playgroud)

使用MAKE-PATHNAME构造路径名

查找以下所有.h文件的三种方法/usr/local/

(directory "/usr/local/**/*.h")

(directory (make-pathname :name :wild
                          :type "h"
                          :defaults "/usr/local/**/")

(directory
   (make-pathname :name :wild
                  :type "h"
                  :directory '(:ABSOLUTE "usr" "local" :WILD-INFERIORS)))
Run Code Online (Sandbox Code Playgroud)

问题

甚至在同一平台(尤其是“ windows”或“ unix”)上,跨平台(“ windows”,“ unix”,“ mac”,...)的实现也有很多不同的解释。路径名中的诸如unicode之类的东西会增加额外的复杂性-在CL标准中没有描述。

我们仍然有很多不同的文件系统(https://en.wikipedia.org/wiki/List_of_file_systems),但是它们的功能与Common Lisp设计时的功能不同或不同。实现可能已经跟踪了更改,但不一定以可移植的方式进行。