Uzyskaj przefiltrowaną listę plików w katalogu


281

Próbuję uzyskać listę plików w katalogu przy użyciu Pythona, ale nie chcę listy WSZYSTKICH plików.

Zasadniczo chcę, aby móc zrobić coś takiego, ale używając Pythona i nie wykonując ls.

ls 145592*.jpg

Jeśli nie ma wbudowanej metody, obecnie myślę o napisaniu pętli for w celu iteracji wyników os.listdir()i dołączenia wszystkich pasujących plików do nowej listy.

Jednak w tym katalogu znajduje się wiele plików i dlatego mam nadzieję, że istnieje bardziej wydajna metoda (lub metoda wbudowana).


[Ten link może ci pomóc :) Uzyskaj przefiltrowaną listę plików w katalogu] ( codereview.stackexchange.com/a/33642 )
sha111

Pamiętaj, że możesz szczególnie uważać na kolejność sortowania, jeśli jest to ważne dla twojej aplikacji.
lędźwiowy

Odpowiedzi:


385

21
Och, właśnie zauważyłem, że dokumenty Pythona mówią, że glob () „jest wykonywany przy użyciu funkcji os.listdir () i fnmatch.fnmatch () w porozumieniu, a nie przez faktyczne wywoływanie podpowłoki”. Innymi słowy, glob () nie ma oczekiwanej poprawy wydajności.
Ben Hoyt

5
Jest jedna główna różnica: glob.glob('145592*.jpg')drukuje całą bezwzględną ścieżkę plików, a ls 145592*.jpgdrukuje tylko listę plików.
Ébe Isaac

8
@Ben Dlaczego wywoływanie podpowłoki (podprocesu) miałoby jakąkolwiek poprawę wydajności?
Paulo Neves,

7
@PauloNeves: prawda, mój komentarz powyżej nie ma dla mnie sensu 7 lat później. :-) Zgaduję, że miałem na myśli fakt, że glob()po prostu używa listdir + fnmatch, a nie specjalnych wywołań systemu operacyjnego do filtrowania symboli wieloznacznych. Na przykład w systemie Windows FindFirstFileinterfejs API umożliwia określenie symboli wieloznacznych, dzięki czemu system operacyjny wykonuje filtrowanie bezpośrednio i prawdopodobnie bardziej wydajnie (nie sądzę, że istnieje odpowiednik w systemie Linux).
Ben Hoyt,

1
@marsh: Jak zawsze bieżący katalog roboczy procesu.
Ignacio Vazquez-Abrams

124

glob.glob()jest zdecydowanie sposobem na zrobienie tego (zgodnie z Ignacio). Jeśli jednak potrzebujesz bardziej skomplikowanego dopasowywania, możesz to zrobić ze zrozumieniem listy i re.match():

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

Bardziej elastyczny, ale jak zauważasz, mniej wydajny.


To zdecydowanie wydaje się silniejsze. Na przykład konieczność zrobienia czegoś takiego[0-9]+
demongolem

3
Tak, zdecydowanie bardziej wydajny - jednak fnmatch obsługuje [0123456789]sekwencje ( patrz dokumentacja ), a także ma fnmatch.filter()funkcję, która sprawia, że ​​ta pętla jest nieco bardziej wydajna.
Ben Hoyt,

49

Nie komplikuj:

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

Wolę tę formę listowego rozumienia, ponieważ dobrze czyta po angielsku.

Czytam czwarty wiersz w następujący sposób: Dla każdego pliku fn w os.listdir mojej ścieżki podaj mi tylko te, które pasują do któregokolwiek z moich dołączonych rozszerzeń.

Początkującym programistom pythonowym może być naprawdę trudno przyzwyczaić się do korzystania ze zrozumiałych list do filtrowania, a także może mieć narzut pamięci w przypadku bardzo dużych zestawów danych, ale w przypadku wyświetlania katalogu i innych prostych zadań filtrowania ciągów, zrozumienie listy prowadzi do większej czystości dokumentowalny kod.

Jedyną rzeczą w tym projekcie jest to, że nie chroni on przed błędem przekazania ciągu zamiast listy. Na przykład, jeśli przypadkowo przekonwertujesz ciąg znaków na listę i skończysz sprawdzanie wszystkich znaków łańcucha, możesz skończyć z mnóstwem fałszywych trafień.

Ale lepiej jest mieć problem, który można łatwo naprawić, niż rozwiązanie trudne do zrozumienia.


5
Nie dlatego, że jest to potrzebne any(), ponieważ str.endswith()wymaga sekwencji zakończeń. if fn.endswith(included_extentensions)jest więcej niż wystarczające.
Martijn Pieters

3
Poza nieefektywnością nieużywania str.endswith(seq)tego wskazanego Martijna, nie jest to poprawne, ponieważ plik musi się kończyć .ext, aby miał to rozszerzenie. Ten kod znajdzie również (na przykład) plik o nazwie „myjpg” lub katalog o nazwie „png”. Aby to naprawić, wystarczy poprzedzić każde rozszerzenie w included_extensionsz ..
Ben Hoyt

Zawsze jestem trochę nieufny w stosunku do kodu, który oczywiście nie został uruchomiony lub nie może zostać uruchomiony. Zmienna included_extensionsvs included_extentsions? Szkoda, bo inaczej to moja preferowana odpowiedź.
Auspice


17

Filtruj za pomocą glob modułu:

Importuj glob

import glob

Dzikie karty:

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

Rozszerzenie Fiter .txt:

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

Pojedyncza postać

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

Zakresy liczbowe

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

Zakresy alfabetyczne

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

12

Kod wstępny

import glob
import fnmatch
import pathlib
import os

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

Rozwiązanie 1 - użyj „glob”

# lookup in current dir
glob.glob(pattern)

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

Rozwiązanie 2 - użyj „os” + „fnmatch”

Wariant 2.1 - Wyszukiwanie w bieżącym reż

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

Wariant 2.2 - Wyszukiwanie rekurencyjne

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

Wynik

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

Rozwiązanie 3 - użyj „pathlib”

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

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

Uwagi:

  1. Testowane na Pythonie 3.4
  2. Moduł „pathlib” został dodany tylko w Pythonie 3.4
  3. Python 3.5 dodał funkcję wyszukiwania rekurencyjnego w glob.glob https://docs.python.org/3.5/library/glob.html#glob.glob . Ponieważ moja maszyna jest zainstalowana w Pythonie 3.4, nie testowałem tego.

9

użyj os.walk do rekurencyjnej listy plików

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)

Nie trzeba kroić; file.endswith(alist_filter)wystarczy.
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")]

To da ci listę plików jpg z pełną ścieżką. Można zastąpić x[0]+"/"+fz fdo tylko nazwy plików. Możesz również zastąpić f.endswith(".jpg")dowolnym wymaganym ciągiem.


3

może ci się spodobać podejście na wyższym poziomie (zaimplementowałem i spakowałem jako 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

można zainstalować za pomocą

pip install findtools

2

Nazwy plików z rozszerzeniami „jpg” i „png” w „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]

Jest to bardzo podobne do odpowiedzi udzielonej przez @ ramsey0
chb

1

Możesz użyć pathlib, który jest dostępny w standardowej bibliotece Pythona 3.4 i nowszych.

from pathlib import Path

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

1

Możesz zdefiniować wzór i sprawdzić go. Tutaj wziąłem wzór początkowy i końcowy i szukam ich w nazwie pliku. PLIKI zawiera listę wszystkich plików w katalogu.

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

Co powiesz na str.split ()? Nic do zaimportowania.

import os

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

2
Jest to bardzo podobne do odpowiedzi udzielonej
Sushanth

To wydaje się być podobne do odpowiedzi @ ramsey0 przy użyciu f.endswith('.jpg')(ale również wybierze filename.jpg.ext)
anjsimmo

-1

Możesz użyć subprocess.check_ouput () jako

import subprocess

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

Oczywiście ciąg między cudzysłowami może być wszystkim, co chcesz wykonać w powłoce i przechowywać dane wyjściowe.


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.