web-dev-qa-db-fra.com

Recherche récursive de sous-dossiers et renvoi de fichiers dans une liste python

Je travaille sur un script pour parcourir récursivement les sous-dossiers d'un dossier principal et créer une liste à partir d'un certain type de fichier. J'ai un problème avec le script. Son actuellement défini comme suit

for root, subFolder, files in os.walk(PATH):
    for item in files:
        if item.endswith(".txt") :
            fileNamePath = str(os.path.join(root,subFolder,item))

le problème est que la variable de sous-dossier extrait une liste de sous-dossiers plutôt que le dossier dans lequel se trouve le fichier ITEM. Je pensais lancer une boucle for pour le sous-dossier avant et rejoindre la première partie du chemin, mais je pensais que je vérifiais si je vérifiais si quelqu'un avait des suggestions avant. Merci de votre aide!

83
user2709514

Vous devriez utiliser le dirpath que vous appelez root. Les dirnames sont fournis afin que vous puissiez le nettoyer s'il existe des dossiers dans lesquels vous ne souhaitez pas que os.walk soit renvoyé.

import os
result = [os.path.join(dp, f) for dp, dn, filenames in os.walk(PATH) for f in filenames if os.path.splitext(f)[1] == '.txt']

Edit:

Après le dernier vote négatif, il m'est apparu que glob était un meilleur outil de sélection par extension.

import os
from glob import glob
result = [y for x in os.walk(PATH) for y in glob(os.path.join(x[0], '*.txt'))]

Aussi une version de générateur

from itertools import chain
result = (chain.from_iterable(glob(os.path.join(x[0], '*.txt')) for x in os.walk('.')))

Edit2 pour Python 3.4 +

from pathlib import Path
result = list(Path(".").rglob("*.[tT][xX][tT]"))
129
John La Rooy

Modifié dans Python 3.5 : Prise en charge des globs récursifs à l’aide de "**".

glob.glob() a un nouveau paramètre récursif .

Si vous voulez obtenir tous les fichiers .txt sous my_path (incluant récursivement des sous-répertoires):

import glob

files = glob.glob(my_path + '/**/*.txt', recursive=True)

# my_path/     the dir
# **/       every file and dir under my_path
# *.txt     every file that ends with '.txt'

Si vous avez besoin d'un itérateur, vous pouvez utiliser iglob comme alternative:

for file in glob.iglob(my_path, recursive=False):
    # ...
70
Rotareti

Je vais traduire compréhension de la liste de John La Rooy en imbriqué, au cas où quelqu'un aurait du mal à le comprendre.

result = [y for x in os.walk(PATH) for y in glob(os.path.join(x[0], '*.txt'))]

Devrait être équivalent à:

import glob

result = []

for x in os.walk(PATH):
    for y in glob.glob(os.path.join(x[0], '*.txt')):
        result.append(y)

Voici la documentation pour compréhension de la liste et les fonctions os.walk et glob.glob .

14
Jefferson Lima

Ce n'est pas la réponse la plus pythonique, mais je vais la mettre ici pour le plaisir, car c'est une bonne leçon de récursion

def find_files( files, dirs=[], extensions=[]):
    new_dirs = []
    for d in dirs:
        try:
            new_dirs += [ os.path.join(d, f) for f in os.listdir(d) ]
        except OSError:
            if os.path.splitext(d)[1] in extensions:
                files.append(d)

    if new_dirs:
        find_files(files, new_dirs, extensions )
    else:
        return

Sur ma machine, j'ai deux dossiers, root et root2

mender@multivax ]ls -R root root2
root:
temp1 temp2

root/temp1:
temp1.1 temp1.2

root/temp1/temp1.1:
f1.mid

root/temp1/temp1.2:
f.mi  f.mid

root/temp2:
tmp.mid

root2:
dummie.txt temp3

root2/temp3:
song.mid

Disons que je veux trouver tous les fichiers .txt et tous les fichiers .mid de l'un ou l'autre de ces répertoires, alors je peux simplement le faire.

files = []
find_files( files, dirs=['root','root2'], extensions=['.mid','.txt'] )
print(files)

#['root2/dummie.txt',
# 'root/temp2/tmp.mid',
# 'root2/temp3/song.mid',
# 'root/temp1/temp1.1/f1.mid',
# 'root/temp1/temp1.2/f.mid']
5
dermen

La nouvelle bibliothèque pathlib simplifie cela en une ligne:

from pathlib import Path
result = list(Path(PATH).glob('**/*.txt'))

Vous pouvez également utiliser la version du générateur:

from pathlib import Path
for file in Path(PATH).glob('**/*.txt'):
    pass

Cela retourne les objets Path, que vous pouvez utiliser pour à peu près n'importe quoi, ou obtenir le nom du fichier sous forme de chaîne par file.name.

5
Emre

Récursif est nouveau dans Python 3.5, il ne fonctionnera donc pas sur Python 2.7. Voici l'exemple qui utilise les chaînes r, il vous suffit donc de fournir le chemin tel qu'il est sur Win, Lin, ...

import glob

mypath=r"C:\Users\dj\Desktop\nba"

files = glob.glob(mypath + r'\**\*.py', recursive=True)
# print(files) # as list
for f in files:
    print(f) # Nice looking single line per file

Remarque: Il répertoriera tous les fichiers, quelle que soit leur profondeur.

0
prosti