Obtenez une liste filtrée de fichiers dans un répertoire


281

J'essaie d'obtenir une liste de fichiers dans un répertoire en utilisant Python, mais je ne veux pas une liste de TOUS les fichiers.

Ce que je veux essentiellement, c'est la possibilité de faire quelque chose comme ce qui suit mais en utilisant Python et sans exécuter ls.

ls 145592*.jpg

S'il n'y a pas de méthode intégrée pour cela, je pense actuellement à écrire une boucle for pour parcourir les résultats d'un os.listdir()et pour ajouter tous les fichiers correspondants à une nouvelle liste.

Cependant, il y a beaucoup de fichiers dans ce répertoire et j'espère donc qu'il existe une méthode plus efficace (ou une méthode intégrée).


[Ce lien pourrait vous aider :) Obtenez une liste filtrée des fichiers dans un répertoire] ( codereview.stackexchange.com/a/33642 )
sha111

Notez que vous pouvez faire particulièrement attention à l'ordre de tri si cela est important pour votre application.
lumbric

Réponses:


385

21
Oh, je viens de remarquer que les documents Python disent que glob () "se fait en utilisant les fonctions os.listdir () et fnmatch.fnmatch () de concert, et non en invoquant réellement un sous-shell". En d'autres termes, glob () n'a pas les améliorations d'efficacité auxquelles on pourrait s'attendre.
Ben Hoyt

5
Il y a une différence principale: glob.glob('145592*.jpg')imprime tout le chemin absolu des fichiers alors ls 145592*.jpgqu'imprime seulement la liste des fichiers.
Ébe Isaac

8
@Ben Pourquoi l'invocation d'un sous-shell (sous-processus) aurait-elle des améliorations d'efficacité?
Paulo Neves

7
@PauloNeves: vrai, mon commentaire ci-dessus n'a pas de sens pour moi 7 ans plus tard non plus. :-) Je suppose que je faisais référence au fait qu'il glob()utilise uniquement listdir + fnmatch, plutôt que des appels spéciaux du système d'exploitation pour effectuer le filtrage générique. Par exemple, sous Windows, l' FindFirstFileAPI vous permet de spécifier des caractères génériques afin que le système d'exploitation effectue le filtrage directement et probablement plus efficacement (je ne pense pas qu'il existe un équivalent sous Linux).
Ben Hoyt

1
@marsh: Comme toujours, le répertoire de travail actuel du processus.
Ignacio Vazquez-Abrams du

125

glob.glob()est certainement la façon de le faire (selon Ignacio). Cependant, si vous avez besoin d'une correspondance plus compliquée, vous pouvez le faire avec une compréhension de liste et re.match(), quelque chose comme ça:

files = [f for f in os.listdir('.') if re.match(r'[0-9]+.*\.jpg', f)]

Plus flexible, mais comme vous le constatez, moins efficace.


Cela semble certainement être plus puissant. Par exemple, avoir à faire quelque chose comme[0-9]+
demongolem

3
Oui, certainement plus puissant - cependant fnmatch prend en charge les [0123456789]séquences ( voir la documentation ), et il a également la fnmatch.filter()fonction qui rend cette boucle légèrement plus efficace.
Ben Hoyt

49

Rester simple:

import os
relevant_path = "[path to folder]"
included_extensions = ['jpg','jpeg', 'bmp', 'png', 'gif']
file_names = [fn for fn in os.listdir(relevant_path)
              if any(fn.endswith(ext) for ext in included_extensions)]

Je préfère cette forme de listes de compréhension car elle se lit bien en anglais.

Je lis la quatrième ligne comme suit: Pour chaque fn dans os.listdir pour mon chemin, donnez-moi uniquement celles qui correspondent à l'une de mes extensions incluses.

Il peut être difficile pour les programmeurs novices de Python de vraiment s'habituer à utiliser des compréhensions de liste pour le filtrage, et il peut y avoir une surcharge de mémoire pour de très grands ensembles de données, mais pour lister un répertoire et d'autres tâches de filtrage de chaîne simples, les compréhensions de liste conduisent à plus de netteté code documentable.

La seule chose à propos de cette conception est qu'elle ne vous protège pas contre l'erreur de passer une chaîne au lieu d'une liste. Par exemple, si vous convertissez accidentellement une chaîne en liste et que vous finissez par vérifier tous les caractères d'une chaîne, vous pourriez finir par obtenir une série de faux positifs.

Mais il vaut mieux avoir un problème facile à résoudre qu'une solution difficile à comprendre.


5
Non pas qu'il y ait un besoin any()ici, car str.endswith()prend une séquence de fins. if fn.endswith(included_extentensions)est plus que suffisant.
Martijn Pieters

3
Mis à part l'inefficacité de ne pas utiliser ce str.endswith(seq)que Martijn a souligné, ce n'est pas correct, car un fichier doit se terminer .extpour qu'il ait cette extension. Ce code trouvera également (par exemple) un fichier appelé "myjpg" ou un répertoire nommé simplement "png". Pour corriger, il suffit de préfixer chaque extension included_extensionsavec un ..
Ben Hoyt

Je me méfie toujours un peu du code dans les réponses qui, de toute évidence, n'ont pas été exécutées ou ne peuvent pas fonctionner. La variable included_extensionsvs included_extentsions? Dommage car sinon c'est ma réponse préférée.
Auspice


17

Filtrer avec glob module:

Importer glob

import glob

Cartes génériques:

files=glob.glob("data/*")
print(files)

Out:

['data/ks_10000_0', 'data/ks_1000_0', 'data/ks_100_0', 'data/ks_100_1',
'data/ks_100_2', 'data/ks_106_0', 'data/ks_19_0', 'data/ks_200_0', 'data/ks_200_1', 
'data/ks_300_0', 'data/ks_30_0', 'data/ks_400_0', 'data/ks_40_0', 'data/ks_45_0', 
'data/ks_4_0', 'data/ks_500_0', 'data/ks_50_0', 'data/ks_50_1', 'data/ks_60_0', 
'data/ks_82_0', 'data/ks_lecture_dp_1', 'data/ks_lecture_dp_2']

Extension du filtre .txt:

files = glob.glob("/home/ach/*/*.txt")

Un seul personnage

glob.glob("/home/ach/file?.txt")

Plages de nombres

glob.glob("/home/ach/*[0-9]*")

Gammes alphabétiques

glob.glob("/home/ach/[a-c]*")

12

Code préliminaire

import glob
import fnmatch
import pathlib
import os

pattern = '*.py'
path = '.'

Solution 1 - utilisez "glob"

# lookup in current dir
glob.glob(pattern)

In [2]: glob.glob(pattern)
Out[2]: ['wsgi.py', 'manage.py', 'tasks.py']

Solution 2 - utilisez "os" + "fnmatch"

Variante 2.1 - Recherche dans le répertoire courant

# lookup in current dir
fnmatch.filter(os.listdir(path), pattern)

In [3]: fnmatch.filter(os.listdir(path), pattern)
Out[3]: ['wsgi.py', 'manage.py', 'tasks.py']

Variante 2.2 - Recherche récursive

# lookup recursive
for dirpath, dirnames, filenames in os.walk(path):

    if not filenames:
        continue

    pythonic_files = fnmatch.filter(filenames, pattern)
    if pythonic_files:
        for file in pythonic_files:
            print('{}/{}'.format(dirpath, file))

Résultat

./wsgi.py
./manage.py
./tasks.py
./temp/temp.py
./apps/diaries/urls.py
./apps/diaries/signals.py
./apps/diaries/actions.py
./apps/diaries/querysets.py
./apps/library/tests/test_forms.py
./apps/library/migrations/0001_initial.py
./apps/polls/views.py
./apps/polls/formsets.py
./apps/polls/reports.py
./apps/polls/admin.py

Solution 3 - utilisez "pathlib"

# lookup in current dir
path_ = pathlib.Path('.')
tuple(path_.glob(pattern))

# lookup recursive
tuple(path_.rglob(pattern))

Remarques:

  1. Testé sur Python 3.4
  2. Le module "pathlib" a été ajouté uniquement dans Python 3.4
  3. Le Python 3.5 a ajouté une fonctionnalité de recherche récursive avec glob.glob https://docs.python.org/3.5/library/glob.html#glob.glob . Puisque ma machine est installée avec Python 3.4, je n'ai pas testé cela.

9

utilisez os.walk pour lister récursivement vos fichiers

import os
root = "/home"
pattern = "145992"
alist_filter = ['jpg','bmp','png','gif'] 
path=os.path.join(root,"mydir_to_scan")
for r,d,f in os.walk(path):
    for file in f:
        if file[-3:] in alist_filter and pattern in file:
            print os.path.join(root,file)

Pas besoin de trancher; file.endswith(alist_filter)est assez.
Martijn Pieters

5
import os

dir="/path/to/dir"
[x[0]+"/"+f for x in os.walk(dir) for f in x[2] if f.endswith(".jpg")]

Cela vous donnera une liste de fichiers jpg avec leur chemin complet. Vous pouvez remplacer x[0]+"/"+fpar fjuste pour les noms de fichiers. Vous pouvez également remplacer f.endswith(".jpg")par n'importe quelle condition de chaîne que vous souhaitez.


3

vous aimerez peut-être aussi une approche de plus haut niveau (j'ai implémenté et empaqueté comme findtools ):

from findtools.find_files import (find_files, Match)


# Recursively find all *.txt files in **/home/**
txt_files_pattern = Match(filetype='f', name='*.txt')
found_files = find_files(path='/home', match=txt_files_pattern)

for found_file in found_files:
    print found_file

peut être installé avec

pip install findtools

2

Noms de fichiers avec les extensions "jpg" et "png" dans "chemin / vers / images":

import os
accepted_extensions = ["jpg", "png"]
filenames = [fn for fn in os.listdir("path/to/images") if fn.split(".")[-1] in accepted_extensions]

Ceci est très similaire à la réponse donnée par @ ramsey0
chb

1

Vous pouvez utiliser pathlib qui est disponible dans la bibliothèque standard Python 3.4 et supérieure.

from pathlib import Path

files = [f for f in Path.cwd().iterdir() if f.match("145592*.jpg")]

1

Vous pouvez définir un modèle et le vérifier. Ici, j'ai pris le motif de début et de fin et je les ai recherchés dans le nom de fichier. FICHIERS contient la liste de tous les fichiers d'un répertoire.

import os
PATTERN_START = "145592"
PATTERN_END = ".jpg"
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
for r,d,FILES in os.walk(CURRENT_DIR):
    for FILE in FILES:
        if PATTERN_START in FILE and PATTERN_END in FILE:
            print FILE

0

Que diriez-vous de str.split ()? Rien à importer.

import os

image_names = [f for f in os.listdir(path) if len(f.split('.jpg')) == 2]

2
Ceci est très similaire à la réponse donnée par @gypsy
Sushanth

Cela semble être similaire à la réponse de @ ramsey0 en utilisant f.endswith('.jpg')(mais sélectionnera également filename.jpg.ext)
anjsimmo

-1

Vous pouvez utiliser subprocess.check_ouput () comme

import subprocess

list_files = subprocess.check_output("ls 145992*.jpg", shell=True) 

Bien sûr, la chaîne entre guillemets peut être tout ce que vous souhaitez exécuter dans le shell et stocker la sortie.


En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.