6.5. Travailler avec des répertoires

Le module os.path a de nombreuses fonctions pour manipuler les chemins de fichiers et de répertoires. Ici nous voulons gérer les chemins et lister le contenu d'un répertoire.

Exemple 6.16. Construction de noms de chemins

>>> import os
>>> os.path.join("c:\\music\\ap\\", "mahadeva.mp3") 1 2
'c:\\music\\ap\\mahadeva.mp3'
>>> os.path.join("c:\\music\\ap", "mahadeva.mp3")   3
'c:\\music\\ap\\mahadeva.mp3'
>>> os.path.expanduser("~")                         4
'c:\\Documents and Settings\\mpilgrim\\My Documents'
>>> os.path.join(os.path.expanduser("~"), "Python") 5
'c:\\Documents and Settings\\mpilgrim\\My Documents\\Python'
1 os.path est une référence à un module, quel module exactement dépend de la plateforme que vous utilisez. Tout comme getpass encapsule les différences entre plateforme en assignant à getpass une fonction spécifique à la plateforme, os encapsule les différences entre plateformes en assignant à path un module spécifique à la plateforme.
2 La fonction join de os.path construit un nom de chemin à partir d’un ou de plusieurs noms de chemins partiels. Dans ce cas simple il ne fait que concaténer des chaînes (notez que traiter des noms de chemins sous Windows est ennuyeux car le backslash force à utiliser le caractère d’échappement).
3 Dans cette exemple un peu moins simple, join ajoute un backslash supplémentaire au nom de chemin avant de le joindre au nom de fichier. J’étais ravi quand j’ai découvert cela car addSlashIfNecessary est une des petites fonctions stupides que je dois toujours écrire quand je construis ma boîte à outil dans un nouveau langage. N’écrivez pas cette petite fonction stupide en Python, des gens intelligents l’ont déjà fait pour vous.
4 expanduser développe un nom de chemin qui utilise ~ pour représenter le répertoire de l’utilisateur. Cela fonctionne sur toutes les plateformes où les utilisateurs ont un répertoire propre comme Windows, UNIX et Mac OS X, c'est sans effet sous Mac OS.
5 En combinant ces techniques, vous pouvez facilement construire des noms de chemins pour les répertoires et les fichiers contenus dans le répertoire utilisateur.

Exemple 6.17. Division de noms de chemins

>>> os.path.split("c:\\music\\ap\\mahadeva.mp3")                        1
('c:\\music\\ap', 'mahadeva.mp3')
>>> (filepath, filename) = os.path.split("c:\\music\\ap\\mahadeva.mp3") 2
>>> filepath                                                            3
'c:\\music\\ap'
>>> filename                                                            4
'mahadeva.mp3'
>>> (shortname, extension) = os.path.splitext(filename)                 5
>>> shortname
'mahadeva'
>>> extension
'.mp3'
1 La fonction split divise un nom de chemin complet et retourne un tuple contenant le chemin et le nom de fichier. Vous vous rappelez quand je vous ai dit que vous pouviez utiliser l’assignement multiple de variables pour retourner des valeurs multiples d’une fonction ? Et bien split est une de ces fonctions.
2 Nous assignons la valeur de retour de la fonction split à un tuple de deux variables. Chaque variable reçoit la valeur de l’élément correspondant du tuple retourné.
3 La première variable, filepath, reçoit la valeur du premier élément du tuple retourné par split, le chemin du fichier.
4 La seconde variable, filename, reçoit la valeur du second élément du tuple retourné par split, le nom de fichier.
5 os.path contient aussi une fonction splitext, qui divise un nom de fichier et retourne un tuple contenant le nom de fichier et l’extension. Nous utilisons la même technique pour assigner chacun d’entre eux à des variables séparées.

Exemple 6.18. Liste des fichiers d’un répertoire

>>> os.listdir("c:\\music\\_singles\\")              1
['a_time_long_forgotten_con.mp3', 'hellraiser.mp3',
'kairo.mp3', 'long_way_home1.mp3', 'sidewinder.mp3', 
'spinning.mp3']
>>> dirname = "c:\\"
>>> os.listdir(dirname)                              2
['AUTOEXEC.BAT', 'boot.ini', 'CONFIG.SYS', 'cygwin',
'docbook', 'Documents and Settings', 'Incoming', 'Inetpub', 'IO.SYS',
'MSDOS.SYS', 'Music', 'NTDETECT.COM', 'ntldr', 'pagefile.sys',
'Program Files', 'Python20', 'RECYCLER',
'System Volume Information', 'TEMP', 'WINNT']
>>> [f for f in os.listdir(dirname)
...     if os.path.isfile(os.path.join(dirname, f))] 3
['AUTOEXEC.BAT', 'boot.ini', 'CONFIG.SYS', 'IO.SYS', 'MSDOS.SYS',
'NTDETECT.COM', 'ntldr', 'pagefile.sys']
>>> [f for f in os.listdir(dirname)
...     if os.path.isdir(os.path.join(dirname, f))]  4
['cygwin', 'docbook', 'Documents and Settings', 'Incoming',
'Inetpub', 'Music', 'Program Files', 'Python20', 'RECYCLER',
'System Volume Information', 'TEMP', 'WINNT']
1 La fonction listdir prend un nom de chemin et retourne une liste du contenu du répertoire.
2 listdir retourne à la fois les fichiers et les répertoires, sans indiquer lequel est quoi.
3 Vous pouvez utiliser le filtrage de liste et la fonction isfile du module os.path pour séparer les fichiers des répertoires. isfile prend un nom de chemin et retourne 1 si le chemin représente un fichier et 0 dans le cas contraire. Ici, nous utilisons os.path.join pour nous assurer que nous avons un nom de chemin complet, mais isfile marche aussi avec des chemins partiels, relatifs au répertoire en cours. Vous pouvez utiliser os.getcwd() pour obtenir le répertoire en cours.
4 os.path a aussi une fonction isdir qui retourne 1 si le chemin représente un répertoire et 0 dans le cas contraire. Vous pouvez l’utiliser pour obtenir une liste des sous-répertoires d’un répertoire.

Exemple 6.19. Liste des fichiers d’un répertoire dans fileinfo.py


def listDirectory(directory, fileExtList):                                        
    "get list of file info objects for files of particular extensions" 
    fileList = [os.path.normcase(f)
                for f in os.listdir(directory)]            1 2
    fileList = [os.path.join(directory, f) 
               for f in fileList
                if os.path.splitext(f)[1] in fileExtList]  3 4 5
1 os.listdir(directory) retourne une liste de tous les fichiers et répertoires de directory.
2 En parcourant la liste avec f, nous utilisons os.path.normcase(f) pour normaliser la casse en fonction des paramètres par défaut du système d’exploitation. normcase est une petite fonction utile qui compense le problème des systèmes d’exploitation insensibles à la casse qui pensent que mahadeva.mp3 et mahadeva.MP3 sont le même fichier. Par exemple, sous Windows et Mac OS, normcase convertit l’ensemble du nom de fichier en minuscules, sous les systèmes compatibles UNIX, elle retourne le nom de fichier inchangé.
3 En parcourant la liste normalisée avec f à nouveau, nous utilisons os.path.splitext(f) pour diviser chaque nom de fichier en nom et extension.
4 Pour chaque fichier, nous regardons si l’extension est dans la liste d’extensions de fichier qui nous intéressent (fileExtList, qui a été passé à la fonction listDirectory).
5 Pour chaque fichier qui nous intéresse, nous utilisons os.path.join(directory, f) pour construire le chemin de fichier complet. Nous retournons une liste de noms de chemin complets.
NOTE
A chaque fois que c’est possible, vous devriez utiliser les fonction de os et os.path pour les manipulations de fichier, de répertoire et de chemin. Ces modules enveloppent des modules spécifiques aux plateformes, les fonctions comme os.path.split marchent donc sous UNIX, Windows, Mac OS et toute autre plateforme supportée par Python.

Il existe une autre manière d'obtenir le contenu d'un répertoire. Elle est très puissante et utilise le type de jokers qui vous sont familier si vous utilisez la ligne de commande.

Exemple 6.20. Liste du contenu d'un répertoire avec glob

>>> os.listdir("c:\\music\\_singles\\")               1
['a_time_long_forgotten_con.mp3', 'hellraiser.mp3',
'kairo.mp3', 'long_way_home1.mp3', 'sidewinder.mp3',
'spinning.mp3']
>>> import glob
>>> glob.glob('c:\\music\\_singles\\*.mp3')           2
['c:\\music\\_singles\\a_time_long_forgotten_con.mp3',
'c:\\music\\_singles\\hellraiser.mp3',
'c:\\music\\_singles\\kairo.mp3',
'c:\\music\\_singles\\long_way_home1.mp3',
'c:\\music\\_singles\\sidewinder.mp3',
'c:\\music\\_singles\\spinning.mp3']
>>> glob.glob('c:\\music\\_singles\\s*.mp3')          3
['c:\\music\\_singles\\sidewinder.mp3',
'c:\\music\\_singles\\spinning.mp3']
>>> glob.glob('c:\\music\\*\\*.mp3')                  4
1 Comme nous l'avons vu plus haut, os.listdir prend simplement un chemin de répertoire et retourne la liste de tous les fichiers et répertoires qu'il contient.
2 Le module glob, par contre, prend un joker et retourne le chemin complet de tous les fichiers et répertoires qui lui correspondent. Ici, le joker est un chemin de répertoire plus "*.mp3", c'est à dire tous les fichiers .mp3. Notez que chaque élément de la liste retournée contient le chemin complet du fichier.
3 Voici le joker pour trouver tous les fichiers d'un répertoire qui commencent par "s" et finissent par ".mp3".
4 Maintenant considerez le scénario suivant : vous avez un répertoire music, contenant plusieurs sous-répertoires, avec des fichiers .mp3 dans chaque sous-répertoire. Vous pouvez obtenir une liste de tous ces fichiers avec un seul appel à glob, en utilisant deux jokers à la fois. Un des jokers est "*.mp3" (qui correspond aux fichiers .mp3) et l'autre est à l'intérieur du chemin de répertoire, ce qui correspond aux sous-répertoires de c:\music. C'est une énorme puissance contenue dans une fonction à l'air faussement simple !

Pour en savoir plus sur le module os