Отримайте відфільтрований список файлів у каталозі


281

Я намагаюся отримати список файлів у каталозі за допомогою Python, але я не хочу список ВСІХ файлів.

Я, по суті, хочу, це можливість робити щось на кшталт наступного, але використовуючи Python, а не виконувати ls.

ls 145592*.jpg

Якщо для цього немає вбудованого методу, я зараз думаю створити цикл для повторення результатів os.listdir()і додати всі відповідні файли до нового списку.

Однак у цьому каталозі багато файлів, і тому я сподіваюся, що існує більш ефективний метод (або вбудований метод).


[Це посилання може допомогти вам :) Отримайте відфільтрований список файлів у каталозі] ( codereview.stackexchange.com/a/33642 )
sha111

Зауважте, що ви можете бути особливо обережними щодо порядку сортування, якщо це важливо для вашої програми.
Мастило

Відповіді:


385

21
О, я щойно помітив, що документи Python говорять, що glob () "робиться за допомогою використання функцій os.listdir () та fnmatch.fnmatch () на концерті, а не шляхом фактичного виклику підзарядки". Іншими словами, glob () не має покращення ефективності, які можна було б очікувати.
Бен Хойт

5
Є одна основна відмінність: glob.glob('145592*.jpg')друкує весь абсолютний шлях файлів, а ls 145592*.jpgдрукує лише список файлів.
Ебе Ісаак

8
@Ben Чому виклик підзаготі (підпроцесу) має покращити ефективність?
Пауло Невес

7
@PauloNeves: правда, мій коментар вище не має сенсу і для мене через 7 років. :-) Я здогадуюсь, що я мав на увазі той факт, що glob()для фільтрації підстановок використовуються лише listdir + fnmatch, а не спеціальні дзвінки операційної системи. Наприклад, в Windows FindFirstFileAPI дозволяє вам задавати підстановку, щоб ОС здійснювала фільтрацію безпосередньо і, імовірно, більш ефективно (я не думаю, що в Linux існує еквівалент).
Бен Хойт

1
@marsh: Як завжди, поточний робочий каталог процесу.
Ігнасіо Васкес-Абрамс

124

glob.glob()це, безумовно, спосіб зробити це (за Ігнасіо). Однак якщо вам потрібне більш складне узгодження, ви можете зробити це з розумінням списку і re.match(), як-от так:

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

Більш гнучка, але, як зазначаєте, менш ефективна.


Це, безумовно, здається більш потужним. Наприклад, робити щось на кшталт[0-9]+
demongolem

3
Так, безумовно, більш потужний - однак fnmatch підтримує [0123456789]послідовності ( див. Документи ), і він також має fnmatch.filter()функцію, яка робить цю петлю трохи ефективнішою.
Бен Хойт

49

Не ускладнювати:

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

Я вважаю за краще цю форму розуміння списку, оскільки вона добре читається англійською мовою.

Я читаю четвертий рядок як: Для кожного fn в os.listdir для мого шляху, дайте мені лише ті, які відповідають будь-якому з моїх включених розширень.

Новачкам-програмістам-пітонам може бути важко реально звикнути до використання списків розуміння для фільтрації, і це може мати деяку накладну пам'ять для дуже великих наборів даних, але для перерахування каталогу та інших простих завдань фільтрації рядків розуміння списку призводить до більш чистого документальний код.

Єдине, що стосується цієї конструкції, це те, що вона не захищає вас від помилки при передачі рядка замість списку. Наприклад, якщо ви випадково перетворили рядок у список і в кінцевому підсумку перевіряєте всі символи рядка, ви можете в результаті отримати неправдиві позитиви.

Але краще мати проблему, яку легко виправити, ніж важко зрозуміти рішення.


5
Не те, що тут є якась потреба any(), бо str.endswith()займає послідовність закінчень. if fn.endswith(included_extentensions)більш ніж достатньо.
Мартійн Пітерс

3
Крім неефективності невикористання, на str.endswith(seq)яку вказував Мартійн, це не вірно, тому що файл повинен закінчуватись .extтим, що має таке розширення. Цей код також знайде (наприклад) файл під назвою "myjpg" або каталог, названий просто "png". Щоб виправити, просто префікс кожне розширення в included_extensionsс ..
Бен Хойт

Я завжди трохи остерігаюся коду у відповідях, які, очевидно, не були запущені або не можуть бути запущені. Змінна included_extensionsvs included_extentsions? Шкода, бо інакше це моя краща відповідь.
Auspice

39

Ще один варіант:

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

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


5
Це саме те, що globробиться в одному рядку.
Ітай Грудев

Єдина відмінність - це globповернення повного шляху на відміну від os.listdirпростого повернення імені файлу. Принаймні, так відбувається в Python 2.
Karthic Raghupathi

17

Фільтр з globмодулем:

Імпортувати глобус

import glob

Дикі картки:

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 .txt:

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

Одиничний персонаж

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

Діапазони чисел

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

Діапазон алфавіту

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

12

Попередній код

import glob
import fnmatch
import pathlib
import os

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

Рішення 1 - використовувати "глобус"

# lookup in current dir
glob.glob(pattern)

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

Рішення 2 - використовувати "os" + "fnmatch"

Варіант 2.1 - Пошук у поточному режимі

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

Варіант 2.2 - пошук рекурсивного

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

Результат

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

Рішення 3 - використовувати "pathlib"

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

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

Примітки:

  1. Тестований на Python 3.4
  2. Модуль "pathlib" був доданий лише в Python 3.4
  3. Python 3.5 додав функцію для рекурсивного пошуку за допомогою glob.glob https://docs.python.org/3.5/library/glob.html#glob.glob . Оскільки моя машина встановлена ​​з Python 3.4, я цього не перевіряв.

9

використовуйте os.walk для рекурсивного списку ваших файлів

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)

Не потрібно скидати; file.endswith(alist_filter)достатньо.
Мартійн Пітерс

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

Це дасть вам список файлів jpg з їх повним шляхом. Ви можете замінити x[0]+"/"+fз fтільки за імена файлів. Ви також можете замінити f.endswith(".jpg")будь-яку умову рядка.


3

Вам також може сподобатися підхід більш високого рівня (я реалізований і упакований як 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

можна встановити за допомогою

pip install findtools


1

Ви можете використовувати pathlib, який доступний у стандартній бібліотеці Python 3.4 і вище.

from pathlib import Path

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

1

Ви можете визначити шаблон і перевірити його. Тут я взяв як початковий, так і кінцевий зразок і шукав їх у імені файлу. ФАЙЛИ містить список усіх файлів у каталозі.

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

Як щодо str.split ()? Нічого не імпортувати.

import os

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

2
Це дуже схоже на відповідь, яку дав @gypsy
Sushanth

Це схоже на відповідь @ ramsey0 за допомогою f.endswith('.jpg')(але також буде вибрано filename.jpg.ext)
anjsimmo

-1

Ви можете використовувати subprocess.check_ouput () як

import subprocess

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

Звичайно, рядок між цитатами може бути будь-чим, що ви хочете виконати в оболонці, і зберегти висновок.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.