2009-09-10 7 views
22

Ich verwende OpenMCL auf Darwin, und ich möchte, wie etwas zu tun ist:Wie durchläuft ich ein Verzeichnis in Common Lisp?

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

Aber ich kann directory nicht bekommen, etwas anderes als NIL zurückzukehren, und ich kann nicht scheinen zu finden, gute Erklärung online (außer "es ist für jedes System anders").

Irgendwelche Zeiger?

Antwort

16

Enthält Ihre Pfadangabe einen Platzhalter? Common Lisp des Pfadname Sachen etwas hart ist zunächst zu erfassen - zumindest für mich war es ... Da die CLHS Staaten auf der directory Funktion:

Wenn die Pfadangabe nicht wild ist, die resultierende Liste enthält entweder Null oder ein Elemente.

Um Ihren Pfad zu haben, umfasst einen Platzhalter, könnten Sie die Make-Pfad-Funktion versuchen, wie

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

Oder sogar

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

ich die CL-FAD Bibliothek eine große Hilfe gefunden für den Umgang mit Pfadnamen und dem Dateisystem. Insbesondere ist die list-directory-Funktion möglicherweise einfacher zu verwenden als die einfache Standardfunktion directory.

+2

Yep für mich gearbeitet - (Verzeichnis "Pfad") zurück NIL, wo (Verzeichnis "Pfadname /*.*" gab mir die erwarteten Ergebnisse. – Justicle

+1

Sie benötigen nur Dateien mit Namen, die einen Punkt enthalten? – Svante

+0

Komisch, nicht wahr? Ich bin eigentlich nach .h und .cpp Dateien, aber "Pfadname/*" gibt NIL zurück. – Justicle

26

Es gibt grundsätzlich zwei Möglichkeiten, Pfadnamen zu spezifizieren:

  • mit Strings

Strings sind offensichtlich auf der Plattform abhängig: Unix Syntax und Windows-Syntax zum Beispiel.

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

Sie können einen Pfad Objekt aus einem String erstellen:

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

Die #p oben stellt sicher, dass ein Pfadobjekt (und kein String) erstellt wird, wenn Sie es wieder lesen.

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

also intern Common Lisp arbeitet mit Pfadnamen Objekten, aber es kann Sie normale Strings verwenden und macht aus ihnen Pfadnamen Objekte, wenn nötig.

Wenn Common Lisp einen Pfadnamen sieht, der nicht alle Komponenten enthält (z. B. das Verzeichnis fehlt), füllt es die Komponenten aus dem Objekt pfadname aus, das den Wert der Variable * DEFAULT-PATHNAME-DEFAULTS * enthält.

Mit der Funktion beschreiben, die Sie bei den Komponenten eines Pfadname aussehen kann (hier 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 
  • die Lisp-Funktionen Erstellen Pfadname

MAKE-PATHNAME Objekte mit ist die Funktion und es braucht ein paar Schlüsselwortargumente, um die Komponenten zu spezifizieren.

Manchmal ist es auch sinnvoll, einen neuen Pfad auf einer bestehenden Basis zu schaffen:

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

Wenn Sie Verzeichnis es sinnvoll ist, einen Pfad mit Platzhalter zu verwenden. DIRECTORY gibt dann eine Liste passender Pfadnamen zurück. Der Name 'DIRECTORY' ist leicht irreführend, da DIRECTORY nicht den Inhalt eines Verzeichnisses auflistet, sondern die passenden Pfadnamen für (normalerweise) einen Pfadnamen mit Platzhaltern auflistet. Die Platzhalter können eine Folge von Zeichen in Komponenten wie /foo/s*c/list*.l* "enthalten. Es gibt auch den Platzhalter **, mit dem Teile einer Verzeichnishierarchie wie/foo/** abgeglichen werden. /test.lisp, das passt auf alle Dateien test.lisp unter dem Verzeichnis foo und dessen Unterverzeichnissen.

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

oben in eine Liste aller 'Lispeln' Dateien '/ Users/foo/Lisp /' zurückkehren sollte und alle seine Unterverzeichnisse

die .c-Dateien in einem einzigen Verzeichnis verwenden, um zurückzukehren.

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

Beachten Sie, dass DIRE CTORY gibt eine Liste von Pfadnamen-Objekten zurück (keine Liste von 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") 

Oben verwendet ein Pfadname-Objekt, das von MAKE-PATHNAME erstellt wird. Es gibt alle Dateien zurück, die mit /Lisp/cl-http/cl-http-342/server/md5.* übereinstimmen.

Dies ist das gleiche wie:

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

, die kürzer ist, hängt aber von der Unix Pfadsyntax.

+0

+1 Eine gute Zusammenfassung von Pfadnamen in LISP, sehr hilfreich. – Justicle

8

Die moderne Common Lisp-Bibliothek, die die Verzeichnisliste implementiert, ist IOLIB.

Es funktioniert wie folgt:

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

Beachten Sie, dass keine Schrägstrich oder Platzhalter erforderlich sind. Es ist sehr robust und kann sogar Dateinamen mit falsch codierten Unicode-Zeichen verarbeiten.

Unterschiede im Vergleich zu CL-FAD:

  • Die Objekte, die Sie erhalten, sind iolib Dateipfade, ein Ersatz für Pfadnamen CL, die näher ist, was das zugrunde liegende Betriebssystem der Fall ist.
  • IOLIB implementiert seine Routinen mit CFFI, also funktioniert es auf allen Lisp-Implementierungen (vorausgesetzt IOLIB hat ein Backend für das Betriebssystem), im Gegensatz zu CL-FAD, das versucht, die DIRECTORY-Funktion der Implementierung mit all ihren zu abstrahieren Macken.
  • Im Gegensatz zu CL-FAD behandelt iolib korrekt Symlinks (ein großes Problem mit CL-FAD, das es auf anderen Plattformen als Windows IMHO praktisch unbrauchbar macht).
1

Ich füge ein Beispiel, das für mich funktioniert, aus Gründen eines Codeausschnitts. Ich benutze osicat (ähnlich wie Cl-Fad) und str.

bearbeiten: auch mit uiop:directory-files. str: enthält? könnte mit search getan werden.

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

kehrt

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

Es kann sicherlich meine eine richtige Verwendung von Wildcards verbessert werden. Aber das ist ein Ausschnitt Sie jetzt verwenden können:)

Referenzen: