Як я можу шукати підпапки за допомогою модуля glob.glob?


107

Я хочу відкрити ряд папок у папці і знайти деякі текстові файли та надрукувати деякі рядки текстових файлів. Я використовую це:

configfiles = glob.glob('C:/Users/sam/Desktop/file1/*.txt')

Але це також не може отримати доступ до папок. Хтось знає, як я можу використовувати ту саму команду для доступу до папок?


Відповіді:


163

У Python 3.5 та новіших версіях використовується нова рекурсивна **/функціональність:

configfiles = glob.glob('C:/Users/sam/Desktop/file1/**/*.txt', recursive=True)

Коли recursiveвстановлено, **після цього роздільник шляху відповідає 0 або більше підкаталогів.

У більш ранніх версіях Python glob.glob()не можна реєструвати рекурсивно файли в підкаталогах.

У такому випадку я б використовував os.walk()комбінований з fnmatch.filter():

import os
import fnmatch

path = 'C:/Users/sam/Desktop/file1'

configfiles = [os.path.join(dirpath, f)
    for dirpath, dirnames, files in os.walk(path)
    for f in fnmatch.filter(files, '*.txt')]

Це дозволить рекурсивно використовувати ваші каталоги та повертати всі абсолютні імена до відповідних .txtфайлів. У цьому конкретному випадку fnmatch.filter()може виникнути надмірність, ви також можете використати .endswith()тест:

import os

path = 'C:/Users/sam/Desktop/file1'

configfiles = [os.path.join(dirpath, f)
    for dirpath, dirnames, files in os.walk(path)
    for f in files if f.endswith('.txt')]

3
Я бачу: glob.glob ('/ шлях до каталогу / * / *. Txt ") працює для мене. Це в основному за допомогою правила оболонки Unix.
Surya

7
@ User123: що не реєструє каталоги рекурсивно . Ви перераховуєте всі текстові файли на одному рівні , але не в інших підкаталогах чи навіть безпосередньо в path to directory.
Martijn Pieters

1
Це не повністю пов’язано, але чому налаштування recursive=Falseразом із **/ функціоналом не забезпечує перелік файлів саме в даній папці, а скоріше для її дітей?
Dr_Zaszuś

@ Dr_Zaszuś: вибачте? **/подає список імен каталогів у поточному робочому каталозі, тому що візерунок закінчується /, а у recursive=Falseвас в основному є подвійний *, що збігається так само, як */і менш ефективний.
Martijn Pieters

@ Dr_Zaszuś: використовуйте, */*якщо вам потрібні всі файли у всіх підкаталогах.
Martijn Pieters

22

Щоб знайти файли в безпосередніх підкаталогах:

configfiles = glob.glob(r'C:\Users\sam\Desktop\*\*.txt')

Для рекурсивної версії, яка перетинає всі підкаталоги, ви можете використовувати **та передавати recursive=True з Python 3.5 :

configfiles = glob.glob(r'C:\Users\sam\Desktop\**\*.txt', recursive=True)

Обидва функції повертаються до списку дзвінків. Ви можете використовувати glob.iglob()для повернення шляхи один за одним. Або скористайтесяpathlib :

from pathlib import Path

path = Path(r'C:\Users\sam\Desktop')
txt_files_only_subdirs = path.glob('*/*.txt')
txt_files_all_recursively = path.rglob('*.txt') # including the current dir

Обидва способи повертають ітератори (ви можете отримати шляхи по одному).


Так, я зрозумів це; але я не сподівався glob()підтримувати зразки в каталогах.
Martijn Pieters

Коментар видалений, я бачу, що він справив неправильне враження; крім того, виправлення включає оновлення документації для **випадку рекурсії. Але для **роботи вам доведеться встановити recursion=Trueвимикач, btw.
Martijn Pieters

20

На цю тему багато плутанини. Дозвольте мені побачити, чи можу я уточнити це (Python 3.7):

  1. glob.glob('*.txt') :відповідає всім файлам, що закінчуються '.txt' у поточному каталозі
  2. glob.glob('*/*.txt') :те саме, що 1
  3. glob.glob('**/*.txt') :відповідає всім файлам, що закінчуються на ".txt" лише в безпосередніх підкаталогах , але не в поточному каталозі
  4. glob.glob('*.txt',recursive=True) :те саме, що 1
  5. glob.glob('*/*.txt',recursive=True) :те саме, що 3
  6. glob.glob('**/*.txt',recursive=True):відповідає всім файлам, що закінчуються на ".txt" у поточному каталозі та у всіх підкаталогах

Тому краще завжди вказувати recursive=True.


1
Це має бути головна відповідь!
Абхік Саркар

17

Пакет glob2 підтримує підказки і досить швидко

code = '''
import glob2
glob2.glob("files/*/**")
'''
timeit.timeit(code, number=1)

На моєму ноутбуці потрібно приблизно 2 секунди, щоб відповідати > 60 000 шляхів до файлів .


9

Ви можете використовувати Formic з Python 2.6

import formic
fileset = formic.FileSet(include="**/*.txt", directory="C:/Users/sam/Desktop/")

Розкриття інформації - я автор цього пакету.


4

Ось адаптована версія, яка забезпечує glob.globфункціональність без використання glob2.

def find_files(directory, pattern='*'):
    if not os.path.exists(directory):
        raise ValueError("Directory not found {}".format(directory))

    matches = []
    for root, dirnames, filenames in os.walk(directory):
        for filename in filenames:
            full_path = os.path.join(root, filename)
            if fnmatch.filter([full_path], pattern):
                matches.append(os.path.join(root, filename))
    return matches

Отже, якщо у вас є така структура dir

tests/files
├── a0
   ├── a0.txt
   ├── a0.yaml
   └── b0
       ├── b0.yaml
       └── b00.yaml
└── a1

Можна зробити щось подібне

files = utils.find_files('tests/files','**/b0/b*.yaml')
> ['tests/files/a0/b0/b0.yaml', 'tests/files/a0/b0/b00.yaml']

Досить велика кількість fnmatchшаблону відповідає самій імені файлу, а не лише файлу.


2

configfiles = glob.glob('C:/Users/sam/Desktop/**/*.txt")

Працює не для всіх випадків, натомість використовуйте glob2

configfiles = glob2.glob('C:/Users/sam/Desktop/**/*.txt")

2

Якщо ви можете встановити пакет glob2 ...

import glob2
filenames = glob2.glob("C:\\top_directory\\**\\*.ext")  # Where ext is a specific file extension
folders = glob2.glob("C:\\top_directory\\**\\")

Усі назви файлів і папок:

all_ff = glob2.glob("C:\\top_directory\\**\\**")  

2

Якщо ви використовуєте Python 3.4+, ви можете використовувати pathlibмодуль. Path.glob()Метод підтримує **шаблон, що означає «цей каталог і все підкаталоги, рекурсивно». Він повертає генератор, що дає Pathоб'єкти для всіх збігаються файлів.

from pathlib import Path
configfiles = Path("C:/Users/sam/Desktop/file1/").glob("**/*.txt")

0

Як вказував Мартійн, глобус може це зробити лише через **оператора, представленого в Python 3.5. Оскільки ОП явно попросив глобальний модуль, наступне поверне ледачий ітератор оцінки, який поводиться аналогічно

import os, glob, itertools

configfiles = itertools.chain.from_iterable(glob.iglob(os.path.join(root,'*.txt'))
                         for root, dirs, files in os.walk('C:/Users/sam/Desktop/file1/'))

Зауважте, що configfilesв цьому підході ви можете повторити лише один раз . Якщо вам потрібен реальний список конфігурацій, які можна використовувати в декількох операціях, вам доведеться створити це явно за допомогою list(configfiles).


0

Команда rglob виконає нескінченну рекурсію вниз до найглибшого підрівню структури вашої директорії. Якщо ви хочете лише один рівень глибокий, тоді не використовуйте його.

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

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

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