Opération courante en informatique et on a tous eu besoin de chercher comment faire une fois.
Soit l’arborescence :
test ├── dossier │ ├── fichier.py │ ├── fichier.txt │ └── pas_un_dossier.txt ├── Dossier │ ├── dOssier │ │ └── faichier │ └── fichiiiiiiiiier ├── .fichier ├── fichier ├── fIchier └── Fichier
Lister le contenu d’un dossier
>>> import os >>> os.listdir('.') ['dossier', 'Dossier', 'fichier', 'fIchier', '.fichier', 'Fichier'] |
On récupère les noms des dossiers et les fichiers, y compris cachés, mais pas les dossiers spéciaux types ..
et .
.
Le type des noms retournés est str
que ce soit en Python 2 ou 3. Ça a l’air cool et homogène comme ça, jusqu’à ce qu’on se souvienne que str
sont des bits en Python 2 et de l’unicode en Python 3. Donc gaffe quand vous portez votre code d’une version à l’autre. Les noms de fichiers contiennent des caractères non ASCII dans la vraie vie vivante.
Si vous voulez récupérer uniquement les dossiers ou les fichiers, il va falloir filtrer :
>>> for element in os.listdir('/tmp/test'): ... if os.path.isdir(element): ... print("'%s' un dossier" % element) ... else: ... print("'%s' est un fichier" % element) 'dossier' un dossier 'Dossier' un dossier 'fichier' est un fichier 'fIchier' est un fichier '.fichier' est un fichier 'Fichier' est un fichier |
Pareil si on veut filtrer par extensions :
>>> for element in os.listdir('/tmp/test/dossier'): ... if element.endswith('.txt'): ... print("'%s' est un fichier texte" % element) ... else: ... print("'%s' n'est pas un fichier texte" % element) 'fichier.txt' est un fichier texte 'pas_un_dossier.txt' est un fichier texte 'fichier.py' n'est pas un fichier texte |
Néanmoins, Python vient avec le module glob
qui permet de demander le listing du contenu d’un dossier en appliquant des filtres Unix :
>>> import glob >>> glob.glob('/tmp/test/dossier/*.txt') ['/tmp/test/dossier/fichier.txt', '/tmp/test/dossier/pas_un_dossier.txt'] >>> glob.glob('./dossier/*.txt') ['./dossier/fichier.txt', './dossier/pas_un_dossier.txt'] |
Mais comme vous pouvez le voir, le comportement n’est pas le même que listdir
: les éléments de la liste sont des chemins relatifs à celui passé en paramètre si il est lui-même relatif, ou absolus si celui passé en paramètre est absolu. N’oubliez donc pas de normaliser l’entrée ou la sortie avec os.path.realpath
qui retournera un chemin canonique.
Même problème Python 2/3 : le type est str
dans les 2 cas.
Parcours récursif
La fonction os.walk
permet de lister récursivement tous les fichiers et les dossiers à partir d’un point dans l’arborescence. C’est un générateur, et sa valeur de retour est un peu particulière et se récupère via unpacking:
for dossier, sous_dossiers, fichiers in os.walk('/tmp/test'): print('##### %s #####' % dossier) print("Sous dossiers : %s" % sous_dossiers) print("Fichiers : %s" % fichiers) ##### /tmp/test ##### Sous dossiers : ['dossier', 'Dossier'] Fichiers : ['fichier', 'fIchier', '.fichier', 'Fichier'] ##### /tmp/test/dossier ##### Sous dossiers : [] Fichiers : ['fichier.txt', 'pas_un_dossier.txt', 'fichier.py'] ##### /tmp/test/Dossier ##### Sous dossiers : ['dOssier'] Fichiers : ['fichiiiiiiiiier'] ##### /tmp/test/Dossier/dOssier ##### Sous dossiers : [] Fichiers : ['faichier'] |
Du coup, si vous voulez avoir la liste des chemin des fichiers absolus, il faut reconstituer à la main :
for dossier, sous_dossiers, fichiers in os.walk('/tmp/test'): for fichier in fichiers: print(os.path.join(dossier, fichier)) /tmp/test/fichier /tmp/test/fIchier /tmp/test/.fichier /tmp/test/Fichier /tmp/test/dossier/fichier.txt /tmp/test/dossier/pas_un_dossier.txt /tmp/test/dossier/fichier.py /tmp/test/Dossier/fichiiiiiiiiier /tmp/test/Dossier/dOssier/faichier |
Encore une fois, c’est du str
partout, alors attention. L’article sur l’encoding en Python reste un des plus consultés de la categ prog.
Avec une lib qui va bien
Je n’ai jamais caché mon amour immodéré pour path.py (qui enterre tous ses concurrents, dont unipath), qui encapsule toutes les opérations des fichiers de manière simple et élégante et qui marche sous Python 2 et 3.
pip install path.py |
Et c’est quand même plus facile pour le parcours récursif :
>>> from path import path >>> for f in path('.').walkfiles(): print f ....: ./dossier/fichier.txt ./dossier/pas_un_dossier.txt ./dossier/fichier.py ./Dossier/fichiiiiiiiiier ./Dossier/dOssier/faichier ./fichier ./fIchier ./.fichier ./Fichier |
En plus, f
est de type path
sous Python 2 et 3. C’est homogène, et ça permet de faire toutes les opérations magiques que ce type permet.
Néanmoins, si vous êtes sous Python 3.4 et que vous ne voulez pas ajouter une dépendance externe, vous pouvez utiliser le module pathlib :
>>> from pathlib import Path >>> for p in Path('.').glob('./**/*'): ... if p.is_file(): ... print(p) fichier fIchier .fichier Fichier dossier/fichier.txt dossier/pas_un_dossier.txt dossier/fichier.py Dossier/fichiiiiiiiiier Dossier/dOssier/faichier |
Je peux me tromper, mais il me semble que c’est plus subtile que cela: le type de chaînes retournées est le même que celui donné en paramêtre:
os.path.join(u'/', u'pouet', 'temp')
retourneu'/pouet/temp'
. (A vérifier pour les fonctions citées dans l’article)“On récupère les nom des dossiers et les fichiers” -> noms
“les éléments de la liste sont des chemin relatifs à ” -> chemins
Des bisous :)
Merci. Des calins !
Vous pouvez trouver un exemple concret d’utilisation à cette adresse.
https://github.com/jhautefeuille/pydouble/blob/master/pydouble/job.py
Il s’agit d’une classe permettant de trouver des fichiers dupliqués et/ou supprimer des fichiers par extension.
il y aussi glob2 pour du glob recursif voir https://pypi.python.org/pypi/glob2