Liste des symboles rencontrés dans ce chapitre:
PATHNAME PATHNAMEP PATHNAME-DIRECTORY PATHNAME-NAME PATHNAME-TYPE PATHNAME-HOST PATHNAME-DEVICE PATHNAME-VERSION NAMESTRING DIRECTORY-NAMESTRING FILE-NAMESTRING MAKE-PATHNAME MERGE-PATHNAMES ENOUGH-NAMESTRING PROBE-FILE RENAME-FILE DELETE-FILE ENSURE-DIRECTORIES-EXIST FILE-WRITE-DATE FILE-AUTHOR FILE-LENGTH FILE-POSITION STRING-STREAM MAKE-STRING-INPUT-STREAM WITH-INPUT-FROM-STRING MAKE-STRING-OUTPUT-STREAM GET-OUTPUT-STREAM-STRING WITH-OUTPUT-TO-STRING MAKE-BROADCAST-STREAM MAKE-CONCATENATED-STREAM MAKE-TWO-WAY-STREAM MAKE-ECHO-STREAM DIRECTORY
Les noms de chemins.
PATHNAME: permet de transformer un chemin en objet pathname.
(pathname "/foo/bar/baz.txt")
#P"/foo/bar/baz.txt" construit l'objet pathname correspondant à la chaîne donnée en argument
noter le #p devant la chaîne de caractères
(defparameter *chemin* (pathname "/foo/bar/baz.txt"))
*CHEMIN* définition d'une variable contenant l'objet pathname.
*chemin*
#P"/foo/bar/baz.txt" la variable contient bien l'objet chemin (pathname)
Pour conntrôler que *chemin* est bien du type pathname:
(pathnamep *chemin*)
T
PATHNAME-DIRECTORY:
(pathname-directory *chemin*)
(:ABSOLUTE "foo" "bar") rend le composant "directory" de l'objet pathname (liste)
PATHNAME-NAME:
(pathname-name *chemin*)
"baz" rend le composant "name" de l'objet pathname (chaîne)
autrement dit: le nom du fichier, s'il est dans le chemin
PATHNAME-TYPE:
(pathname-type *chemin*)
"txt" rend le composant "type" de l'objet pathname (chaîne)
autrement dit: l'extension du fichier, si elle est dans le chemin
PATHNAME-HOST:
(pathname-host *chemin*)
#
rend la lettre désignant le disque sous windows
PATHNAME-DEVICE:
(pathname-device *chemin*)
NIL rend le composant "device" de l'objet pathname
ou NIL s'il n'y en a pas (ici, sous Linux)
rend la lettre désignant le disque sous windows
PATHNAME-VERSION:
(pathname-version *chemin*)
:NEWEST rend le composant "version" de l'objet pathname
NAMESTRING: permet de convertir un pathname en chemin sous forme d'une chaîne de caractères
(namestring *chemin*)
"/foo/bar/baz.txt" rend le chemin sous forme d'une chaîne
DIRECTORY-NAMESTRING:
(directory-namestring *chemin*)
"/foo/bar/" rend le chemin du répertoire sous forme d'une chaîne
FILE-NAMESTRING:
(file-namestring *chemin*)
"baz.txt" rend le nom du fichier et son extension sous forme d'une chaîne
MAKE-PATHNAME: permet de construire un pathname (comme PATHNAME), mais en détaillant ses composants
(make-pathname
:directory '(:absolute "foo" "bar")
:name "baz"
:type "txt")
#P"/foo/bar/baz.txt" rend le pathname construit
Suivant l'OS utilisé, la portabilité des programmes peut poser problème: les pathnames sont corrects dans certaines implémentations et pas dans d'autres. Question à approfondir! Voir aussi l'utilisation de l'argument-clé :default (voir plus loin).
MERGE-PATHNAMES: permet de combiner des pathnames
(merge-pathnames #p"foo/bar.html" #p"/www/html/")
#P"/www/html/foo/bar.html" on obtient un pathname absolu. En effet:
(pathname-directory (merge-pathnames #p"foo/bar.html" #p"/www/html/"))
(:ABSOLUTE "www" "html" "foo")
(merge-pathnames #p"foo/bar.html" #p"html/")
#P"html/foo/bar.html" on obtient un pathname relatif, car:
(pathname-directory (merge-pathnames #p"foo/bar.html" #p"html/"))
(:RELATIVE "html" "foo")
Le symbole / fait la différence.
ENOUGH-NAMESTRING:
(enough-namestring #p"/www/html/foo/bar.html" #p"/www/")
"html/foo/bar.html" permet d'obtenir le chemin relatif à un répertoire particulier
(Ici, on obtient le chemin relatif au répertoire /www/)
(merge-pathnames
(enough-namestring #p"/www/html/foo/bar/baz.html" #p"/www/")
#p"/www-backups/")
#P"/www-backups/html/foo/bar/baz.html"
combinaison de ENOUGH-NAMESTRING et MERGE-PATHNAMES
Donc: extraction du chemin relatif à /www/ pour construire le chemin absolu #P"/www-backups/html/foo/bar/baz.html"
*default-pathname-defaults*
#P"/home/yannick/lisp/lispbox/" variable contenant une valeur par défault, suivant l'implémentation
Si on fait:
(make-pathname :name "foo" :type "txt")
#P"foo.txt" ce pathname ne sera pas utilisable par OPEN, le composant directory,
par exemple, n'étant pas là. On peut, alors, utiliser MERGE-PATHNAMES avec un seul argument:
(merge-pathnames #p"foo.txt")
#P"/home/yannick/lisp/lispbox/foo.txt" le 2ème argument est complété par la variable
*default-pathname-defaults*
REMARQUE: Unix et Windows considèrent les répertoires comme une sorte de fichier, ce qui mène à deux pathnames différents:
(make-pathname :directory '(:absolute "foo") :name "bar") donne la forme fichier
(make-pathname :directory '(:absolute "foo" "bar")) donne la forme répertoire
On comprend, alors, que si le répertoire courant de l'utilisateur est "/home/pierre" (répertoire-utilisateur), la construction du pathname suivant:
(make-pathname :name "foo" :type "txt" :defaults répertoire-utilisateur)
permettra de sauver le fichier dans /home/foo.txt au lieu de /home/pierre/foo.txt.
Il peut alors être utile de convertir un pathname dans la forme répertoire à l'aide d'une fonction appelée, disons, pathname-as-directory. L'expression précédente devient:
(make-pathname
:name "foo" :type "txt" :defaults (pathname-as-directory répertoire-utilisateur))
donne le "bon" pathname
(defparameter *pathname*
(make-pathname :directory '(:absolute "home" "yannick" "lisp" "lispbox") :name "name" :type "txt"))
*PATHNAME* définition d'un pathname pour la suite
PROBE-FILE:
(probe-file *pathname*)
#P"/home/yannick/lisp/lispbox/name.txt" rend le chemin et le nom du fichier s'il existe
(probe-file "lispbox/nom.txt") ici, seul le chemin relatif est fourni
#P"/home/yannick/lisp/lispbox/nom.txt" rend le chemin et le nom du fichier s'il existe
(defparameter *bidon*
(make-pathname :directory '(:absolute "home" "yannick" "lisp" "lispbox") :name "bidon" :type "txt"))
*BIDON* définition d'un pathname sachant que le fichier "bidon.txt" n'existe pas
(probe-file *bidon*)
NIL rend NILL, car le fichier n'existe pas
Remarque: toutes les implémentations ne supportent pas l'utilisation de PROBE-FILE pour tester si un répertoire existe.
Et Common Lisp ne fournit pas une méthode portable pour vérifier si le fichier existant est bien un fichier normal ou un répertoire. Il faudra donc encapsuler PROBE-FILE dans une nouvelle fonction, disons, FILE-EXISTS-P, qui, à la fois, testera que le répertoire existe et si le nom donné est celui d'un fichier ou d'un répertoire.
De même la fonction DIRECTORY n'est pas portable. Il faudra définir une fonction
LIST-DIRECTORY.
(setf *chemin* (make-pathname :directory (pathname-directory *pathname*) :name "nom" :type "txt"))
#P"/home/yannick/lisp/lispbox/nom.txt" construit un nouveau pathname et le nomme *chemin*
RENAME-FILE:
(rename-file *pathname* *chemin*)
#P"/home/yannick/lisp/lispbox/nom.txt"
#P"/home/yannick/lisp/lispbox/name.txt"
#P"/home/yannick/lisp/lispbox/nom.txt" renomme le fichier name.txt en nom.txt
DELETE-FILE: prend un pathname, efface le fichier ainsi désigné, rend T ou FILE-ERROR.
ENSURE-DIRECTORIES-EXIST: fonction prenant comme argument un pathname et assurant que tous les composants-répertoires existent, les créant si nécessaire.
Rend ce pathname ainsi vérifié ou créé. Ce qui peut être pratique:
(with-open-file (flux (ensure-directories-exist name) :direction :output) ...)
rem: le pathname doit être sous la forme répertoire.
FILE-WRITE-DATE:
(file-write-date *chemin*)
3604238896 rend le nombre de secondes depuis le 1er janvier 1900 à la date de dernière
écriture du fichier
FILE-AUTHOR:
(file-author *chemin*)
"yannick" rend le nom du propriétaire du fichier
FILE-LENGTH: prend un flux en argument.
(with-open-file (flux *chemin* :element-type '(unsigned-byte 8))
(file-length flux))
112 rend la longueur du fichier "nom.txt" défini par le pathname *chemin* en
octets (112 octets)
FILE-POSITION: prend un ou deux arguments, le 1er est un flux, le 2ème désigne la position
de l'index et peut être :start, ou :end, ou un entier non négatif.
(with-open-file (flux *chemin*)
(file-position flux))
0 à l'ouverture du fichier, l'index est à 0
(with-open-file (flux *chemin*)
(format t "~a~%" (read-line flux)) (file-position flux))
(a b c d e)
12 après lecture d'une ligne, l'index est à 12
(with-open-file (flux *chemin*)
(format t "~a~%" (read-line flux)) (file-position flux :start) (format t "~a~%" (read-line flux)))
(a b c d e)
(a b c d e)
NIL après lecture d'une ligne, l'index est remis à 0, puis la même ligne est
relue
(with-open-file (flux *chemin*)
(file-position flux 12) (format t "~a~%" (read-line flux)))
654
NIL l'index est, d'abord, positionné à 12, c'est, donc, la ligne suivante qui
est lue
Remarque: on peut positionner l'index à la fin du fichier avec: (file-position flux :end).
Autres sortes d'ENTREES/SORTIES:
STRING-STREAM:
MAKE-STRING-INPUT-STREAM: prend, en argument, une chaîne de caractère et d'optionnelles valeurs d'index de début et de fin. WITH-INPUT-FROM-STRING semble plus pratique.
S'utilise avec READ-CAR, READ-LINE ou READ.
(let ((s (make-string-input-stream "1.23")))
(unwind-protect (read s) ;UNWIND-PROTECT assure la continuité du programme et la fermeture du flux en cas d'erreur
(close s)))
1.23 rend un nombre à la place de la chaîne
(let ((s (make-string-input-stream "1.23" 1)))
(unwind-protect (read s)
(close s)))
0.23 l'index de début étant à 1, le "1" de la chaîne n'est pas pris en compte
(let ((s (make-string-input-stream "1.23" 1 3)))
(unwind-protect (read s)
(close s)))
0.2 l'index de fin étant à 3, le "3" de la chaîne n'est pas pris en compte
WITH-INPUT-FROM-STRING: prend, en argument, une chaîne de caractère et les mots-clés :start et :end indiquant le début et la fin de la sous-chaîne, contrairement à MAKE-STRING-INPUT-STRING.
S'utilise avec READ-CAR, READ-LINE ou READ.
(with-input-from-string (s "1.23")
(read s))
1.23 rend un nombre à la place de la chaîne
(with-input-from-string (s "1.23" :start 1)
(read s))
0.23 l'index de début étant à 1, le "1" de la chaîne n'est pas pris en compte
(with-input-from-string (s "1.23" :start 1 :end 3)
(read s))
0.2 l'index de fin étant à 3, le "3" de la chaîne n'est pas pris en compte
MAKE-STRING-OUTPUT-STREAM: ne prend pas d'argument et s'utilise avec FORMAT, PRINT, WRITE-CHAR, WRITE-LINE, ect.
Tout ce qui est écrit est placé dans une chaîne de caractères qui peut être récupéré avec
la fonction:
GET-OUTPUT-STREAM-STRING:
(let ((a-stream (make-string-output-stream)) (a-string "abcdefghijklm"))
(format t "J'écris dans le flux: ~a~%" (write-string a-string a-stream))
(format t "Je récupère dans le flux: ~a~%" (get-output-stream-string a-stream))
(format t "Il n'y a plus rien: ~a~%" (get-output-stream-string a-stream)))
J'écris dans le flux: abcdefghijklm
Je récupère dans le flux: abcdefghijklm
Il n'y a plus rien:
NIL
WITH-OUTPUT-TO-STRING:
(with-output-to-string (out)
(format out "hello, world ")
(format out "~s" (list 1 2 3)))
"hello, world (1 2 3)"
MAKE-BROADCAST-STREAM: constructeur de système de flux sortants.
(setq a-stream (make-string-output-stream)
b-stream (make-string-output-stream))
#
(format (make-broadcast-stream a-stream b-stream) "ceci doit se retrouver dans les deux flux.")
NIL construit un système de deux flux sortants
format envoie dans ces flux une chaîne de caractères
(get-output-stream-string a-stream)
"ceci doit se retrouver dans les deux flux." vérifie que le 1er flux contient la chaîne
(get-output-stream-string b-stream)
"ceci doit se retrouver dans les deux flux." vérifie que le 2ème flux contient la chaîne
(open-stream-p a-stream)
T vérifie si a-stream est bien un flux ouvert
(output-stream-p a-stream)
T vérifie si a-stream est bien un flux sortant (vrai, ici)
(input-stream-p a-stream)
NIL vérifie si a-stream est bien un flux entrant (faux, ici)
(stream-element-type a-stream)
CHARACTER rend le type de a-stream
MAKE-CONCATENATED-STREAM: constructeur de système de flux entrants.
MAKE-TWO-WAY-STREAM: constructeur d'un système de flux avec deux arguments: un flux entrant et un flux sortant.
rend un flux du type approprié qui peut être utilisé avec les fonctions d'entrée et de
sortie. Tout ce qui est lu, l'est depuis le flux entrant. Tout ce qui est écrit, l'est dans
le flux sortant.
MAKE-ECHO-STREAM: même remarque, sauf que tout ce qui est lu depuis le flux entrant est écrit dans le flux sortant.
DIRECTORY: renvoie une liste de pathnames. Admet 2 mots-clés :wild et :newest.
Rend NIL si rien ne correspond à la requète.
DIRECTORY peut poser des problèmes de portabilité.
(directory (pathname "/home/yannick/lisp/*"))
(#P"/home/yannick/lisp/A lire" #P"/home/yannick/lisp/A lire~"
#P"/home/yannick/lisp/lispbox/")
(directory (pathname "/home/yannick/lisp/*.*"))
(#P"/home/yannick/lisp/#helloworld.lisp#" #P"/home/yannick/lisp/4-pp.pdf"
#P"/home/yannick/lisp/A lire" #P"/home/yannick/lisp/A lire~"
#P"/home/yannick/lisp/Common Lisp Caen.odt" #P"/home/yannick/lisp/ch10.pdf"
#P"/home/yannick/lisp/ch11.pdf" #P"/home/yannick/lisp/ch9bis.pdf"
#P"/home/yannick/lisp/helloworld.lisp" #P"/home/yannick/lisp/lispbox/"
#P"/home/yannick/lisp/prog_lisp.ai.univ-paris8.pdf")
Sachant que:
*default-pathname-defaults* contient:
#P"/home/yannick/lisp/"
L'expression suivante donne la même chose:
(directory (make-pathname :name :wild :type :wild))
(#P"/home/yannick/lisp/#helloworld.lisp#" #P"/home/yannick/lisp/4-pp.pdf"
#P"/home/yannick/lisp/A lire" #P"/home/yannick/lisp/A lire~"
#P"/home/yannick/lisp/Common Lisp Caen.odt" #P"/home/yannick/lisp/ch10.pdf"
#P"/home/yannick/lisp/ch11.pdf" #P"/home/yannick/lisp/ch9bis.pdf"
#P"/home/yannick/lisp/helloworld.lisp" #P"/home/yannick/lisp/lispbox/"
#P"/home/yannick/lisp/prog_lisp.ai.univ-paris8.pdf")
De même avec:
(directory (make-pathname :name :wild :type :wild :defaults home-dir))
si home-dir est un pathname représentant
/home/yannick/lisp/
Sauf si home-dir est sous la forme-fichier! auquel cas, c'est la liste des fichiers du répertoire /home/yannick/ qui est
rendue.
***********************************************
ESSAI DE PORTABILITE DE FONCTIONS: (voir DIRECTORY et PROBE-FILE plus haut -- faire CTRL+R probe --)
Les fonctions suivantes (définies plus bas) sont à ajouter à l'implémentation:
COMPONENT-PRESENT-P, DIRECTORY-PATHNAME-P, PATHNAME-AS-DIRECTORY, DIRECTORY-WILDCARD et LIST-DIRECTORY.
Utilisation de #+ et #- en relation avec la variable *features*:
Si l'expression de configuration suivant #+ est vraie, l'expression qui suit est lue et évaluée sinon elle est sautée.
Si l'expression de configuration qui suit #- est fausse, l'expression qui suit est lue et évaluée sinon elle est sautée.
L'expression de configuration est construite avec les booléens NOT, AND et OR, et des mots. Si un mot se trouve dans
la liste de la variable feature, il prend la valeur vraie (T), sinon il prend la valeur fausse (NIL).
Exemple:
Voici le contenu de la variable *features*:
*features*
(:SWANK :SB-BSD-SOCKETS-ADDRINFO :ASDF2 :ASDF :X86 :UNIX :ELF :LINUX :LARGEFILE
:GENCGC :STACK-GROWS-DOWNWARD-NOT-UPWARD :C-STACK-IS-CONTROL-STACK
:COMPARE-AND-SWAP-VOPS :UNWIND-TO-FRAME-AND-CALL-VOP :RAW-INSTANCE-INIT-VOPS
:STACK-ALLOCATABLE-CLOSURES :STACK-ALLOCATABLE-VECTORS
:STACK-ALLOCATABLE-LISTS :STACK-ALLOCATABLE-FIXED-OBJECTS :ALIEN-CALLBACKS
:CYCLE-COUNTER :INLINE-CONSTANTS :MEMORY-BARRIER-VOPS :MULTIPLY-HIGH-VOPS
:LINKAGE-TABLE :LITTLE-ENDIAN :OS-PROVIDES-DLOPEN :OS-PROVIDES-DLADDR
:OS-PROVIDES-PUTWC :OS-PROVIDES-BLKSIZE-T :OS-PROVIDES-SUSECONDS-T
:OS-PROVIDES-GETPROTOBY-R :OS-PROVIDES-POLL :IEEE-FLOATING-POINT
:SB-SOURCE-LOCATIONS :SB-EVAL :SB-UNICODE :SB-PACKAGE-LOCKS :SB-LDB :SB-TEST
:SB-DOC :SBCL :COMMON-LISP :ANSI-CL :SB-CORE-COMPRESSION :SB-THREAD :SB-FUTEX)
(defun foo ()
#+allegro (format t "allegro-OK~%")
#+sbcl (format t "sbcl-OK~%")
#+common-lisp (format t "clisp-OK~%")
#+cmu (format t "cmu-OK~%")
#-(or allegro sbcl common-lisp cmu) (error "non implémenté"))
FOO définition d'une fonction vérifiant la présence des configurations allegro, sbcl, common-lisp, cmu
(foo)
sbcl-OK sbcl et common-lisp sont, seuls, présents dans *features* (donc dans cette implémentation)
clisp-OK
NIL (OR allegro sbcl common-lisp cmu) prend la valeur T (puisque sbcl est T), donc l'erreur est sautée et NIL est rendu
REMARQUE: clisp est, ici, implémenté sous le nom de common-lisp.
COMPONENT-PRESENT-P: fonction à définir.
(defun component-present-p (value)
(and value (not (eql value :unspecific))))
définit une fonction qui vérifie si une valeur est présente (donc non-NIL) et
n'est pas une valeur "spéciale"
(component-present-p "coucou")
T la valeur existe (évidemment) et n'est pas spéciale
(component-present-p (pathname-name *dir*))
NIL la valeur n'existe pas dans *dir*: *dir* ne contient pas de composant "name"
(component-present-p (pathname-name *chemin*))
T une valeur existe dans ce *chemin* (un fichier appelé "nom" ici)
DIRECTORY-PATHNAME-P: fonction à définir.
(defun directory-pathname-p (p)
(and
(not (component-present-p (pathname-name p)))
(not (component-present-p (pathname-type p)))
p))
définit une fonction qui vérifie que l'argument est bien un répertoire et qui rend ce répertoire si vrai, NIL sinon
(directory-pathname-p *dir*)
#P"/home/yannick/lisp/lispbox/" *dir* est bien un répertoire
(directory-pathname-p *chemin*)
NIL *chemin* n'est pas un répertoire (contient "nom.txt")
WILD-PATHNAME-P: vérifie si un pathname contient des caractères joker (caractères génériques)
(wild-pathname-p (make-pathname :directory (pathname-directory *dir*) :name :wild :type :wild))
(:WILD :WILD-INFERIORS) :wild est un caractère générique (ici la réponse est T)
On peut passer un argument optionnel, un champ-clé, comme :host, :device, :directory, :name, :type ou :version.
(wild-pathname-p (make-pathname :directory (pathname-directory *dir*) :name :wild :type :wild) :name)
(:WILD :WILD-INFERIORS) :name contient un joker
(wild-pathname-p (make-pathname :directory (pathname-directory *dir*) :name :wild :type :wild) :directory)
NIL :directory n'est pas un joker, donc NIL
PATHNAME-AS-DIRECTORY: fonction à définir.
(defun pathname-as-directory (name) ;name peut être un pathname, une chaîne ou un symbole, ou un flux
(let ((pathname (pathname name))) ;l'argument est transformé en pathname et placé dans une variable locale
(when (wild-pathname-p pathname) ;pathname contient-il des jokers?
(error "on ne peut pas convertir,sérieusement, un pathname avec joker.")) ;si oui: erreur
(if (not (directory-pathname-p name)) ;si name n'est pas un répertoire (pourquoi pas pathname?)
(make-pathname ;on le reconstruit correctement
:directory (append (or (pathname-directory pathname) (list :relative))
(list (file-namestring pathname)))
:name nil
:type nil
:defaults pathname) ;et, ce nouveau répertoire est rendu
pathname))) ;sinon, le répertoire est rendu tel quel
PATHNAME-AS-DIRECTORY définit une fonction qui transforme un pathname en répertoire
Une particularité de clisp fait que DIRECTORY ne rendra pas de fichier sans extension,sauf si le composant type est NIL
plutôt que :wild. D'où la fonction suivante:
DIRECTORY-WILDCARD: fonction à définir
(defun directory-wildcard (dirname)
(make-pathname
:name :wild
:type #-clisp :wild #+clisp nil
:defaults (pathname-as-directory dirname)))
DIRECTORY-WILDCARD définit une fonction qui permet de remplacer le joker :wild par NIL, uniquement dans clisp
Il y a quelques autres particularités, suivant les implémentations, pour afficher les sous-répertoires. D'où, finalement,
la fonction suivante:
LIST-DIRECTORY: fonction à définir.
(defun list-directory (dirname)
(when (wild-pathname-p dirname)
(error "LIST-DIRECTORY n'admet pas les jokers.")) ;dirname ne doit pas contenir de jokers
(let ((wildcard (directory-wildcard dirname))) ;variable locale contenant les jokers
#+(or sbcl cmu lispworks)
(directory wildcard) ;utilisation normale de directory pour 3 implémentations
#+openmcl
(directory wildcard :directories t) ;particularité de openmcl
#+allegro
(directory wildcard :directories-are-files nil) ;particularité de allegro
#+clisp
(nconc
(directory wildcard)
(directory (clisp-subdirectories-wildcard wildcard))) ;particularité de clisp (voir plus bas)
#-(or sbcl cmu lispworks openmcl allegro clisp) ;erreur pour les autres implémentations
(error "list-directory n'existe pas dans l'implémentation que vous utilisez.")))
LIST-DIRECTORY définition d'une fonction permettant l'affichage des sous-répertoires
Attention! CLISP-SUBDIRECTORIES-WILDCARD n'est pas définie dans clisp. Cette fonction pourra être définie dans les conditions de démarrage par:
#+clisp
(defun clisp-subdirectories-wildcard (wildcard)
(make-pathname
:directory (append (pathname-directory wildcard) (list :wild))
:name nil
:type nil
:defaults wildcard))
FILE-EXISTS-P: fonction à définir
(defun file-exists-p (pathname)
#+(or sbcl lispworks openmcl)
(probe-file pathname)
#+(or allegro cmu)
(or (probe-file (pathname-as-directory pathname))
(probe-file pathname))
#+clisp
(or (ignore-errors
(probe-file (pathname-as-file pathname))) ;pathname-as-file est définie plus bas
(ignore-errors
(let ((directory-form (pathname-as directory pathname)))
(when (ext:probe-directory directory-form)
directory-form))))
#-(or sbcl cmu lispworks openmcl allegro clisp)
(error "file-exist-p n'est pas implémenté"))
FILE-EXISTS-P
PATHNAME-AS-FILE: fonction à définir
(defun pathname-as-file (name)
(let ((pathname (pathname name)))
(when (wild-pathname-p pathname)
(error "On ne peut pas convertir un pathname avec joker."))
(if (directory-pathname-p name)
(let* ((directory (pathname-directory pathname))
(name-and-type (pathname (first (last directory)))))
(make-pathname
:directory (butlast directory)
:name (pathname-name name-and-type)
:type (pathname-type name-and-type)
:defaults pathname))
pathname)))
PATHNAME-AS-FILE
WALK-DIRECTORY: fonction à définir (voir ci-dessous), prenant en argument le nom d'un répertoire et une fonction. Elle appelle la fonction pour tous les pathnames des fichiers sous ce répertoire, de façon récursive. On peut ajouter deux mots-clés en argument :directories et :test. Quand :directories est true, elle appelle la fonction pour les répertoires au même titre que les fichiers. Le mot-clé :test, s'il est fournit, précise une autre fonction qui est invoquée sur chaque pathname avant la fonction principale; la fonction principale ne sera appelée que si la fonction-test retourne T.
(defun walk-directory (dirname fn &key directories (test (constantly t)))
(labels
((walk (name)
(cond
((directory-pathname-p name)
(when (and directories (funcall test name))
(funcall fn name))
(dolist (x (list-directory name)) (walk x)))
((funcall test name) (funcall fn name)))))
(walk (pathname-as-directory dirname))))
Exemples:
(walk-directory *dir* 'print)
#P"/home/yannick/lisp/lispbox/#ensembles#"
#P"/home/yannick/lisp/lispbox/#fonctions-Clisp#"
#P"/home/yannick/lisp/lispbox/.#fonctions-Clisp"
#P"/home/yannick/lisp/lispbox/chaine-car"
#P"/home/yannick/lisp/lispbox/constructeur"
#P"/home/yannick/lisp/lispbox/ensembles"
#P"/home/yannick/lisp/lispbox/fonctions-Clisp"
#P"/home/yannick/lisp/lispbox/fonctions-Clisp~"
#P"/home/yannick/lisp/lispbox/name.txt~"
#P"/home/yannick/lisp/lispbox/nom.txt"
#P"/home/yannick/lisp/lispbox/proc.el"
#P"/home/yannick/lisp/lispbox/proc.elc"
#P"/home/yannick/lisp/lispbox/proc.el~"
#P"/home/yannick/lisp/lispbox/procedures~"
#P"/home/yannick/lisp/lispbox/stat"
#P"/home/yannick/lisp/lispbox/transf-listes"
#P"/home/yannick/lisp/lispbox/tri"
#P"/home/yannick/lisp/lispbox/vecteurs"
NIL imprime les fichiers et leurs chemins du répertoire *dir*
****************************************
La prochaine fois: STRUCTURES.
Aucun commentaire:
Enregistrer un commentaire