Ottieni un elenco filtrato di file in una directory


295

Sto cercando di ottenere un elenco di file in una directory utilizzando Python, ma non voglio un elenco di TUTTI i file.

Quello che voglio essenzialmente è la capacità di fare qualcosa di simile al seguente ma usando Python e non eseguendo ls.

ls 145592*.jpg

Se non esiste un metodo integrato per questo, sto attualmente pensando di scrivere un ciclo for per iterare i risultati di un os.listdir()e per aggiungere tutti i file corrispondenti a un nuovo elenco.

Tuttavia, ci sono molti file in quella directory e quindi spero che esista un metodo più efficiente (o un metodo integrato).


[Questo collegamento potrebbe aiutarti :) Ottieni un elenco filtrato di file in una directory] ( codereview.stackexchange.com/a/33642 )
sha111

Tieni presente che potresti prestare particolare attenzione all'ordinamento se questo è importante per la tua applicazione.
lumbric

Risposte:


403
import glob

jpgFilenamesList = glob.glob('145592*.jpg')

Vedi globnella documentazione di Python


21
Oh, ho appena notato che i documenti di Python dicono che glob () "viene fatto usando le funzioni os.listdir () e fnmatch.fnmatch () insieme, e non invocando effettivamente una subshell". In altre parole, glob () non ha i miglioramenti di efficienza che ci si potrebbe aspettare.
Ben Hoyt,

5
C'è una differenza principale: glob.glob('145592*.jpg')stampa l'intero percorso assoluto dei file mentre ls 145592*.jpgstampa solo l'elenco dei file.
Ébe Isaac

8
@ Ben Perché invocare una subshell (sottoprocesso) avrebbe miglioramenti in termini di efficienza?
Paulo Neves

7
@PauloNeves: vero, il mio commento sopra non ha senso neanche per me 7 anni dopo. :-) Immagino che mi riferissi al fatto che glob()usa solo listdir + fnmatch, piuttosto che chiamate speciali del sistema operativo per fare il filtro con caratteri jolly. Ad esempio, su Windows l' FindFirstFileAPI consente di specificare i caratteri jolly in modo che il sistema operativo esegua il filtraggio direttamente e presumibilmente in modo più efficiente (non credo che ci sia un equivalente su Linux).
Ben Hoyt

2
Non dimenticare di usareimport glob
Sky

130

glob.glob()è sicuramente il modo per farlo (come per Ignacio). Tuttavia, se hai bisogno di abbinamenti più complicati, puoi farlo con una comprensione dell'elenco e re.match(), qualcosa del genere:

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

Più flessibile, ma come noti, meno efficiente.


Questo sembra decisamente essere più potente. Ad esempio, dover fare qualcosa come[0-9]+
demongolem

3
Sì, decisamente più potente, tuttavia fnmatch supporta le [0123456789]sequenze ( vedere la documentazione ) e ha anche la fnmatch.filter()funzione che rende questo ciclo leggermente più efficiente.
Ben Hoyt

56

Mantienilo semplice:

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)]

Preferisco questa forma di comprensione delle liste perché si legge bene in inglese.

Ho letto la quarta riga come: Per ogni fn in os.listdir per il mio percorso, dammi solo quelli che corrispondono a una qualsiasi delle mie estensioni incluse.

Può essere difficile per i programmatori Python alle prime armi abituarsi davvero a usare le comprensioni delle liste per il filtraggio, e può avere un certo sovraccarico di memoria per set di dati molto grandi, ma per elencare una directory e altre semplici attività di filtraggio delle stringhe, le comprensioni delle liste portano a una maggiore pulizia codice documentabile.

L'unica cosa su questo design è che non ti protegge dal commettere l'errore di passare una stringa invece di un elenco. Ad esempio, se converti accidentalmente una stringa in un elenco e finisci per controllare tutti i caratteri di una stringa, potresti finire per ottenere una sfilza di falsi positivi.

Ma è meglio avere un problema facile da risolvere che una soluzione difficile da capire.


5
Non che ce ne sia bisogno any()qui, perché str.endswith()richiede una sequenza di finali. if fn.endswith(included_extentensions)è più che sufficiente.
Martijn Pieters

3
A parte l'inefficienza di non usare str.endswith(seq)che Martijn ha sottolineato, questo non è corretto, perché un file deve terminare con .extperché abbia quell'estensione. Questo codice troverà anche (per esempio) un file chiamato "myjpg" o una directory chiamata semplicemente "png". Per risolvere il problema, anteponi a ciascuna estensione included_extensionsun ..
Ben Hoyt

Sono sempre un po 'diffidente nei confronti del codice nelle risposte che ovviamente non è stato eseguito o non può essere eseguito. La variabile included_extensionsvs included_extentsions? Un vero peccato perché altrimenti questa è la mia risposta preferita.
Auspice

41

Un'altra opzione:

>>> import os, fnmatch
>>> fnmatch.filter(os.listdir('.'), '*.py')
['manage.py']

https://docs.python.org/3/library/fnmatch.html


5
Questo è esattamente ciò che globfa su una singola riga.
Itay Grudev

1
L'unica differenza è globrestituire il percorso completo anziché os.listdirrestituire solo il nome del file. Almeno questo è ciò che sta accadendo in Python 2.
k427h1c

20

Filtra con globmodulo:

Importa glob

import glob

Carte jolly:

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']

Estensione Fiter .txt:

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

Un unico personaggio

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

Intervalli di numeri

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

Gamme di alfabeto

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

12

Codice preliminare

import glob
import fnmatch
import pathlib
import os

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

Soluzione 1 : usa "glob"

# lookup in current dir
glob.glob(pattern)

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

Soluzione 2 : usa "os" + "fnmatch"

Variante 2.1 - Ricerca nella directory corrente

# 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 - Ricerca ricorsiva

# 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))

Risultato

./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

Soluzione 3 : usa "pathlib"

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

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

Appunti:

  1. Testato su Python 3.4
  2. Il modulo "pathlib" è stato aggiunto solo in Python 3.4
  3. Python 3.5 ha aggiunto una funzionalità per la ricerca ricorsiva con glob.glob https://docs.python.org/3.5/library/glob.html#glob.glob . Poiché la mia macchina è installata con Python 3.4, non l'ho testato.

9

usa os.walk per elencare in modo ricorsivo i tuoi file

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)

Non c'è bisogno di affettare; file.endswith(alist_filter)è abbastanza.
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")]

Questo ti darà un elenco di file jpg con il loro percorso completo. Puoi sostituire x[0]+"/"+fcon fsolo i nomi dei file. Puoi anche sostituire f.endswith(".jpg")con qualsiasi condizione di stringa desideri.


4

ti potrebbe piacere anche un approccio più di alto livello (ho implementato e impacchettato come 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

può essere installato con

pip install findtools

2

Nomi di file con estensioni "jpg" e "png" in "path / to / images":

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

Questo è molto simile alla risposta data da @ ramsey0
chb

2

Puoi usare pathlib disponibile nella libreria standard Python 3.4 e successive.

from pathlib import Path

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

1

Puoi definire il modello e verificarlo. Qui ho preso sia il pattern iniziale che quello finale e li ho cercati nel nome del file. FILES contiene l'elenco di tutti i file in una directory.

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

1

Che ne dici di str.split ()? Niente da importare.

import os

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

2
Questo è molto simile alla risposta data da @gypsy
sushanth

1
Questo sembra essere simile alla risposta di @ ramsey0 usando f.endswith('.jpg')(ma selezionerà anche filename.jpg.ext)
anjsimmo

-1

Puoi usare subprocess.check_ouput () come

import subprocess

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

Ovviamente, la stringa tra virgolette può essere qualsiasi cosa tu voglia eseguire nella shell e memorizzare l'output.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.