Получить отфильтрованный список файлов в каталоге


281

Я пытаюсь получить список файлов в каталоге, используя Python, но мне не нужен список ВСЕХ файлов.

По сути, я хочу сделать что-то вроде следующего, но использовать Python, а не выполнять ls.

ls 145592*.jpg

Если для этого нет встроенного метода, я сейчас подумываю написать цикл for для итерации по результатам os.listdir()и добавления всех соответствующих файлов в новый список.

Однако в этом каталоге много файлов, и поэтому я надеюсь, что есть более эффективный метод (или встроенный метод).


[Эта ссылка может помочь вам :) Получить отфильтрованный список файлов в каталоге] ( codereview.stackexchange.com/a/33642 )
sha111

Обратите внимание, что вы можете позаботиться о порядке сортировки, если это важно для вашего приложения.
lumbric

Ответы:


385

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

5
Есть одно основное отличие: glob.glob('145592*.jpg')печатает полный абсолютный путь к файлам, а ls 145592*.jpgпечатает только список файлов.
Ébe Исаак

8
@Ben Почему вызов подоболочки (подпроцесса) имеет какие-либо улучшения эффективности?
Пауло Невес

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

1
@marsh: Как всегда, текущий рабочий каталог процесса.
Игнасио Васкес-Абрамс

125

glob.glob()это определенно способ сделать это (согласно Игнасио). Однако, если вам нужно более сложное сопоставление, вы можете сделать это с пониманием списка и re.match(), что-то вроде этого:

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

Более гибкий, но, как вы заметили, менее эффективный.


Это определенно кажется более мощным. Например, делать что-то вроде[0-9]+
демонголем

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 для моего пути, дайте мне только те, которые соответствуют любому из моих включенных расширений.

Для начинающих программистов на python может быть трудно привыкнуть к использованию списочных представлений для фильтрации, и это может иметь некоторые накладные расходы памяти для очень больших наборов данных, но для перечисления каталога и других простых задач фильтрации строк, списочные понимания приводят к более чистым документируемый код.

Единственное, что есть в этом дизайне, это то, что он не защищает вас от ошибки при передаче строки вместо списка. Например, если вы случайно конвертируете строку в список и заканчиваете проверкой всех символов строки, вы можете получить массу ложных срабатываний.

Но лучше иметь проблему, которую легко решить, чем решение, которое трудно понять.


5
Не то, чтобы здесь была какая-то необходимость any(), потому что она str.endswith()имеет последовательность окончаний. if fn.endswith(included_extentensions)более чем достаточно
Мартин Питерс

3
Помимо неэффективности неиспользования, на str.endswith(seq)которое указал Мартейн, это не правильно, потому что файл должен заканчиваться .extтем, чтобы иметь это расширение. Этот код также найдет (например) файл с именем «myjpg» или каталог с именем «png». Чтобы исправить, просто добавьте каждое расширение included_extensionsк ..
Бен Хойт

Я всегда немного опасаюсь кода в ответах, которые явно не запускались или не запускаются. Переменная included_extensionsпротив 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

Вы можете определить шаблон и проверить его. Здесь я взял и начальный и конечный шаблон и ищу их в имени файла. FILES содержит список всех файлов в каталоге.

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
Сушант

Похоже, это похоже на ответ @ 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.