Как мне перебирать каталог в Common Lisp?
-
05-07-2019 - |
Вопрос
Я использую OpenMCL в Дарвине и хотел бы сделать что-то вроде:
(loop for f in (directory "somedir")
collect (some-per-file-processing f))
Но я не могу получить directory
вернуть что-либо, кроме NIL
, и я не могу найти в Интернете какого-либо хорошего объяснения (кроме «для каждой системы оно разное»).
Есть какие-нибудь указатели?
Решение
Содержит ли ваша спецификация пути подстановочный знак? Путь к путевым именам Common Lisp поначалу довольно сложно понять - по крайней мере, для меня это было так ... CLHS сообщает о функции directory
:
Если 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
.
Другие советы
Есть два основных способа указать пути:
- использование строк
Строки, очевидно, зависят от платформы:Синтаксис 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 возвращает список объектов пути (а не список строк).
? (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.
Современная библиотека Common Lisp, реализующая листинг каталогов, ИОЛИБ.
Это работает следующим образом:
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")
Обратите внимание, что косая черта или подстановочные знаки не требуются.Он очень надежен и может даже обрабатывать имена файлов с неправильно закодированными символами Юникода.
Отличия от CL-FAD:
- Объекты, которые вы получаете, — это пути к файлам IOLIB, замена имен путей CL, которая ближе к тому, что делает базовая ОС.
- IOLIB реализует свои процедуры с использованием CFFI, поэтому он работает одинаково во всех реализациях Lisp (при условии, что IOLIB имеет серверную часть для операционной системы), в отличие от CL-FAD, который пытается абстрагироваться от функции DIRECTORY реализации со всеми ее причудами.
- В отличие от CL-FAD, iolib правильно обрабатывает символические ссылки (одна серьезная проблема с CL-FAD, которая делает его практически непригодным для использования на платформах, отличных от Windows, ИМХО).
Я добавлю пример, который работает для меня, ради фрагмента кода.я использую осикат (аналог cl-fad) и ул..
редактировать:также с uiop:directory-files
.ул: содержит?можно было бы сделать с 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")
Конечно, это можно улучшить за счет правильного использования подстановочных знаков.Однако этот фрагмент вы можете использовать прямо сейчас:)
Использованная литература: