1
votes

comment faire une boucle à travers les dossiers à fond? python

Je suis novice en python et je suis coincé par un problème que j'ai rencontré lors de l'étude des boucles et de la navigation dans les dossiers.

La tâche est simple: parcourir un dossier et compter tous les fichiers '.txt'.

Je crois qu'il peut y avoir des modules pour s'attaquer facilement à cette tâche et j'apprécierais que vous puissiez les partager. Mais comme il ne s'agit que d'une question aléatoire que j'ai rencontrée lors de l'apprentissage de python, ce serait bien si cela peut être résolu en utilisant les outils que je viens d'acquérir, comme les boucles for / while.

J'ai utilisé des clauses for et while pour parcourez un dossier. Cependant, je ne parviens pas à parcourir entièrement un dossier.

Voici le code que j'ai utilisé:

import os
count=0 # set count default
path = 'E:\\' # set path
while os.path.isdir(path):
    for file in os.listdir(path): # loop through the folder
        print(file)   # print text to keep track the process
        if file.endswith('.txt'):
            count+=1
            print('+1')   #
        elif os.path.isdir(os.path.join(path,file)): #if it is a subfolder
            print(os.path.join(path,file))
            path=os.path.join(path,file)
            print('is dir')
            break
        else:
            path=os.path.join(path,file)

Étant donné le nombre de fichiers et de sous-dossiers dans un dossier est inconnu, je pense qu'une boucle while est appropriée ici. Cependant, mon code comporte de nombreuses erreurs ou pièges que je ne sais pas comment résoudre. par exemple, si plusieurs sous-dossiers existent, ce code ne bouclera que le premier sous-dossier et ignorera le reste.


0 commentaires

5 Réponses :


0
votes

Vous voudrez probablement appliquer récursivité à ce problème. En bref, vous aurez besoin d'une fonction pour gérer les répertoires qui s'appelleront quand il rencontrera un sous-répertoire.


1 commentaires

La récursivité n'est pas strictement nécessaire (et en Python, qui a une profondeur de pile limitée qui pourrait être surchargée par des arborescences de répertoires trop profondes, ce n'est pas une bonne idée). Tout ce dont vous avez vraiment besoin est une pile de chemins à parcourir; une simple liste fournit cette fonctionnalité sans récursivité.



0
votes

pour les répertoires imbriqués, il est plus facile d'utiliser des fonctions telles que os.walk prenez ceci par exemple

subfiles = []
for dirpath, subdirs, files in os.walk(path):
    for x in files:
        if x.endswith(".txt"):
            subfiles.append(os.path.join(dirpath, x))`

et il renverra une liste de tous les fichiers txt sinon, vous devrez utiliser la récursivité pour une tâche comme celle-ci


0 commentaires

3
votes

Votre problème est que vous finissez rapidement par essayer de regarder des fichiers inexistants. Imaginez une structure de répertoires où un non-répertoire nommé A ( E: \ A ) est vu en premier, puis un fichier b ( E: \ b ).

Sur votre première boucle, vous obtenez A , vous détectez qu'il ne se termine pas par .txt , et qu'il s'agit d'un répertoire, vous changez donc le chemin en E:\A.

Lors de votre deuxième itération, vous obtenez b (signifiant E: \ b ), mais tous vos tests (à part le test d'extension .txt ) et opérations le concaténent avec le nouveau chemin , donc vous testez par rapport à E: \ A \ b , pas E:\b.

De même, si E: \ A code> est un répertoire, vous cassez la boucle interne immédiatement, donc même si E: \ c.txt existe, si cela se produit après A dans l'ordre d'itération, vous ne le voyez même jamais.

Le code de traversée de l'arborescence de répertoires doit impliquer une pile quelconque, soit explicitement (en ajouter ing et pop code> ing à partir d'une liste de répertoires pour un traitement éventuel), ou implicitement (via la récursivité, qui utilise la pile d'appels pour atteindre le même objectif).

Dans tous les cas, votre cas spécifique devrait vraiment être traité avec os.walk :

import os
count = 0  # set count default
paths = ['E:\\']  # Make stack of paths to process
while paths:
    # paths.pop() gets top of directory stack to process
    # os.scandir is easier and more efficient than os.listdir,
    # though it must be closed (but with statement does this for us)
    with os.scandir(paths.pop()) as entries:
        for entry in entries:  # loop through the folder
            print(entry.name)  # print text to keep track the process
            if entry.name.endswith('.txt'):
                count += 1
                print('+1')
            elif entry.is_dir():  #if it is a subfolder
                print(entry.path, 'is dir')
                # Add to paths stack to get to it eventually
                paths.append(entry.path)

Juste à titre d'illustration, l'approche de pile explicite de votre code serait quelque chose comme:

for root, dirs, files in os.walk(path):
    print(root) # print text to keep track the process
    count += sum(1 for f in files if f.endswith('txt'))

    # This second line matches your existing behavior, but might not be intended
    # Remove it if directories ending in .txt should not be included in the count
    count += sum(1 for d in files if d.endswith('txt'))


2 commentaires

L'os.walk () fonctionne avec élégance. Par curiosité, connaissez-vous le code de os.walk () (ou la façon de trouver le code d'une fonction)? J'ai essayé de parcourir la documentation du module os sans succès.


@Muchdecal: L'ensemble de la base de code CPython est disponible sur GitHub. C'est celui de os.walk code source . Si vous utilisez ipython de manière interactive (je le recommande vivement en remplacement d'une utilisation interactive normale), vous pouvez voir le code source de toute fonction implémentée en Python (mais pas le code d'extension C) avec le < pseudo-opérateur code> ?? , par ex. os.walk ?? .



0
votes

Cela peut être plus que ce dont vous avez besoin, mais cela vous permettra de lister tous les fichiers du répertoire qui sont des fichiers .txt, mais vous pouvez également ajouter des critères à la recherche dans les fichiers. Voici la fonction:

search_df = file_search(root = r'E:\\',
                        search=['foo','bar'], #words to search for
                        extension = 'txt',    #could change this to 'csv' or 'sql' etc.
                        search_type = 'all')  #use any or all

search_df

Voici un exemple d'utilisation de la fonction:

def file_search(root,extension,search,search_type):
    import pandas as pd
    import os
    col1 = []
    col2 = []
    rootdir = root
    for subdir, dirs, files in os.walk(rootdir):
        for file in files:
            if "." + extension in file.lower():
                try:
                    with open(os.path.join(subdir, file)) as f:
                        contents = f.read()
                    if search_type == 'any':
                        if any(word.lower() in contents.lower() for word in search):
                            col1.append(subdir)
                            col2.append(file)

                    elif search_type == 'all':
                        if all(word.lower() in contents.lower() for word in search):
                            col1.append(subdir)
                            col2.append(file)
                except:
                    pass
    df = pd.DataFrame({'Folder':col1,
                      'File':col2})[['Folder','File']]
    return df


0 commentaires

0
votes

L'analyse de votre code a déjà été assez bien traitée par la réponse de @ ShadowRanger. Je vais essayer de répondre à cette partie de votre question:

il peut y avoir des modules pour s'attaquer facilement à cette tâche

Pour ce genre de tâches, il existe en fait le module glob , qui implémente l'extension de modèle de chemin de style Unix.

Pour compter le nombre de fichiers .txt dans un répertoire et tous ses sous-répertoires, un peut simplement utiliser ce qui suit:

import os
from glob import iglob, glob  

dirpath = '.'  # for example

# getting all matching elements in a list a computing its length
len(glob(os.path.join(dirpath, '**/*.txt'), recursive=True))
# 772

# or iterating through all matching elements and summing 1 each time a new item is found
# (this approach is more memory-efficient)
sum(1 for _ in iglob(os.path.join(dirpath, '**/*.txt'), recursive=True))
# 772

Fondamentalement, glob.iglob () est la version itératrice de glob.glob () code >.


0 commentaires