Ruft eine gefilterte Liste der Dateien in einem Verzeichnis ab

281

Ich versuche mit Python eine Liste der Dateien in einem Verzeichnis abzurufen, möchte aber keine Liste ALLER Dateien.

Was ich im Wesentlichen möchte, ist die Fähigkeit, etwas wie das Folgende zu tun, aber Python zu verwenden und ls nicht auszuführen.

ls 145592*.jpg

Wenn es dafür keine integrierte Methode gibt, denke ich derzeit darüber nach, eine for-Schleife zu schreiben, um die Ergebnisse einer os.listdir()zu durchlaufen und alle übereinstimmenden Dateien an eine neue Liste anzuhängen.

Es gibt jedoch viele Dateien in diesem Verzeichnis, und daher hoffe ich, dass es eine effizientere Methode (oder eine integrierte Methode) gibt.

mhost
quelle
[Dieser Link könnte Ihnen helfen :) Holen Sie sich eine gefilterte Liste von Dateien in einem Verzeichnis] ​​( codereview.stackexchange.com/a/33642 )
sha111
Beachten Sie, dass Sie bei der Sortierreihenfolge besonders vorsichtig sein können, wenn dies für Ihre Anwendung wichtig ist.
Lumbric

Antworten:

385

glob.glob('145592*.jpg')

Ignacio Vazquez-Abrams
quelle
21
Oh, ich habe gerade bemerkt, dass in den Python-Dokumenten glob () "verwendet wird, indem die Funktionen os.listdir () und fnmatch.fnmatch () gemeinsam verwendet werden und nicht tatsächlich eine Subshell aufgerufen wird". Mit anderen Worten, glob () hat nicht die erwarteten Effizienzverbesserungen.
Ben Hoyt
5
Es gibt einen Hauptunterschied: glob.glob('145592*.jpg')Gibt den gesamten absoluten Pfad der Dateien aus, während ls 145592*.jpgnur die Liste der Dateien gedruckt wird.
Ébe Isaac
8
@Ben Warum sollte das Aufrufen einer Subshell (eines Subprozesses) zu Effizienzverbesserungen führen?
Paulo Neves
7
@PauloNeves: stimmt, mein Kommentar oben macht für mich auch 7 Jahre später keinen Sinn. :-) Ich glob()schätze, ich habe mich auf die Tatsache bezogen, dass nur listdir + fnmatch verwendet wird, anstatt spezielle Betriebssystemaufrufe, um die Platzhalterfilterung durchzuführen. Unter Windows FindFirstFilekönnen Sie beispielsweise mit der API Platzhalter angeben, damit das Betriebssystem die Filterung direkt und vermutlich effizienter durchführt (unter Linux gibt es meines Erachtens kein Äquivalent).
Ben Hoyt
1
@marsh: Wie immer das aktuelle Arbeitsverzeichnis des Prozesses.
Ignacio Vazquez-Abrams
124

glob.glob()ist definitiv der Weg, es zu tun (gemäß Ignacio). Wenn Sie jedoch einen komplizierteren Abgleich benötigen, können Sie dies mit einem Listenverständnis tun und so re.match()etwas wie:

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

Flexibler, aber wie Sie bemerken, weniger effizient.

Ben Hoyt
quelle
Dies scheint definitiv mächtiger zu sein. Zum Beispiel, so etwas tun zu müssen[0-9]+
demongolem
3
Ja, definitiv leistungsfähiger - jedoch unterstützt fnmatch [0123456789]Sequenzen ( siehe Dokumente ) und hat auch die fnmatch.filter()Funktion, die diese Schleife etwas effizienter macht.
Ben Hoyt
49

Halte es einfach:

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

Ich bevorzuge diese Form des Listenverständnisses, weil es sich gut auf Englisch liest.

Ich lese die vierte Zeile wie folgt: Geben Sie für jedes fn in os.listdir für meinen Pfad nur diejenigen an, die mit einer meiner enthaltenen Erweiterungen übereinstimmen.

Es kann für unerfahrene Python-Programmierer schwierig sein, sich wirklich an die Verwendung von Listenverständnissen zum Filtern zu gewöhnen, und es kann einen gewissen Speicheraufwand für sehr große Datenmengen verursachen, aber für das Auflisten eines Verzeichnisses und anderer einfacher Aufgaben zum Filtern von Zeichenfolgen führen Listenverständnisse zu einer saubereren Liste dokumentierbarer Code.

Das einzige an diesem Design ist, dass es Sie nicht vor dem Fehler schützt, eine Zeichenfolge anstelle einer Liste zu übergeben. Wenn Sie beispielsweise versehentlich eine Zeichenfolge in eine Liste konvertieren und am Ende alle Zeichen einer Zeichenfolge überprüfen, kann dies zu einer Reihe von Fehlalarmen führen.

Es ist jedoch besser, ein Problem zu haben, das leicht zu beheben ist, als eine Lösung, die schwer zu verstehen ist.

ramsey0
quelle
5
Nicht, dass es hier nötig wäre any(), denn es str.endswith()dauert eine Folge von Endungen. if fn.endswith(included_extentensions)ist mehr als genug.
Martijn Pieters
3
Abgesehen von der Ineffizienz, str.endswith(seq)die Martijn nicht verwendet hat, ist dies nicht korrekt, da eine Datei enden .extmuss, damit sie diese Erweiterung hat. Dieser Code findet auch (zum Beispiel) eine Datei mit dem Namen "myjpg" oder ein Verzeichnis mit dem Namen "png". Um dies zu beheben, stellen Sie jeder Erweiterung einfach included_extensionsein vor ..
Ben Hoyt
Ich bin immer ein bisschen vorsichtig mit Code in Antworten, die offensichtlich nicht ausgeführt wurden oder nicht ausgeführt werden können. Die Variable included_extensionsvs included_extentsions? Schade, denn sonst ist dies meine bevorzugte Antwort.
Auspice
39

Andere Option:

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

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

Risadinha
quelle
5
Dies ist genau das, was globin einer einzelnen Zeile geschieht.
Itay Grudev
Der einzige Unterschied besteht darin, dass globder vollständige Pfad zurückgegeben wird und nicht os.listdirnur der Dateiname. Zumindest passiert dies in Python 2.
Karthic Raghupathi
17

Filter mit globModul:

Glob importieren

import glob

Platzhalter:

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

Fiter-Erweiterung .txt:

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

Ein einzelnes Zeichen

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

Nummernkreise

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

Alphabetische Bereiche

glob.glob("/home/ach/[a-c]*")
pink.slash
quelle
12

Vorläufiger Code

import glob
import fnmatch
import pathlib
import os

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

Lösung 1 - Verwenden Sie "glob"

# lookup in current dir
glob.glob(pattern)

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

Lösung 2 - Verwenden Sie "os" + "fnmatch"

Variante 2.1 - Suche im aktuellen Verzeichnis

# 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 - Suche rekursiv

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

Ergebnis

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

Lösung 3 - Verwenden Sie "pathlib"

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

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

Anmerkungen:

  1. Getestet auf Python 3.4
  2. Das Modul "pathlib" wurde nur in Python 3.4 hinzugefügt
  3. Python 3.5 hat eine Funktion für die rekursive Suche mit glob.glob https://docs.python.org/3.5/library/glob.html#glob.glob hinzugefügt . Da mein Computer mit Python 3.4 installiert ist, habe ich das nicht getestet.
PADYMKO
quelle
9

Verwenden Sie os.walk, um Ihre Dateien rekursiv aufzulisten

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)
Ghostdog74
quelle
Keine Notwendigkeit zu schneiden; file.endswith(alist_filter)reicht.
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")]

Dadurch erhalten Sie eine Liste der JPG-Dateien mit ihrem vollständigen Pfad. Sie können ersetzen x[0]+"/"+fmit fnur Dateinamen für. Sie können auch durch eine f.endswith(".jpg")beliebige Zeichenfolgenbedingung ersetzen .

EvgenijM86
quelle
3

Vielleicht möchten Sie auch einen übergeordneten Ansatz (den ich als findtools implementiert und verpackt habe ):

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

kann mit installiert werden

pip install findtools
Yauhen Yakimovich
quelle
2

Dateinamen mit den Erweiterungen "jpg" und "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]
Zigeuner
quelle
Dies ist sehr ähnlich zu der Antwort von @ ramsey0
chb
1

Sie können pathlib verwenden , das in der Python-Standardbibliothek 3.4 und höher verfügbar ist.

from pathlib import Path

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

Sie können Muster definieren und danach suchen. Hier habe ich sowohl Start- als auch Endmuster genommen und sie im Dateinamen gesucht. DATEIEN enthält die Liste aller Dateien in einem Verzeichnis.

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
Rishi Bansal
quelle
0

Wie wäre es mit str.split ()? Nichts zu importieren.

import os

image_names = [f for f in os.listdir(path) if len(f.split('.jpg')) == 2]
Selvaraj Andavar
quelle
2
Dies ist der Antwort von @gypsy
Sushanth
Dies scheint ähnlich zu sein wie die Antwort von @ ramsey0 mit f.endswith('.jpg')(wird aber auch ausgewählt filename.jpg.ext)
anjsimmo
-1

Sie können subprocess.check_ouput () als verwenden

import subprocess

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

Natürlich kann die Zeichenfolge zwischen Anführungszeichen alles sein, was Sie in der Shell ausführen und die Ausgabe speichern möchten.

David A.
quelle
Nur ein Problem. lsDie Ausgabe sollte nicht analysiert werden .
ivan_pozdeev