Как я могу искать подпапки с помощью модуля glob.glob в Python?
Я хочу открыть ряд подпапок в папке, найти несколько текстовых файлов и распечатать несколько строк текстовых файлов. Я использую это:
configfiles = glob.glob('C:/Users/sam/Desktop/file1/*.txt')
Но это также не может получить доступ к подпапкам. Кто-нибудь знает, как я могу использовать ту же команду для доступа к подпапкам, а?
9 ответов
В 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')]
По этой теме много путаницы. Дайте мне посмотреть, смогу ли я это прояснить (Python 3.7):
glob.glob('*.txt') :
соответствует всем файлам, заканчивающимся на '.txt' в текущем каталогеglob.glob('*/*.txt') :
как 1glob.glob('**/*.txt') :
соответствует всем файлам, оканчивающимся на '.txt', только в непосредственных подкаталогах, но не в текущем каталогеglob.glob('*.txt',recursive=True) :
как 1glob.glob('*/*.txt',recursive=True) :
как 3glob.glob('**/*.txt',recursive=True):
соответствует всем файлам, заканчивающимся на '.txt', в текущем каталоге и во всех подкаталогах
Так что лучше всегда указывать recursive=True.
Чтобы найти файлы в непосредственных подкаталогах:
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
Оба метода возвращают итераторы (вы можете получить пути один за другим).
Пакет glob2 поддерживает подстановочные знаки и достаточно быстр
code = '''
import glob2
glob2.glob("files/*/**")
'''
timeit.timeit(code, number=1)
На моем ноутбуке требуется примерно 2 секунды, чтобы сопоставить >60000 путей к файлам.
Вы можете использовать Formic с Python 2.6
import formic
fileset = formic.FileSet(include="**/*.txt", directory="C:/Users/sam/Desktop/")
Раскрытие - я автор этого пакета.
(Первые варианты, конечно, упоминаются в других ответах, здесь цель - показать, что glob использует
os.scandir
внутренне, и дайте им прямой ответ).
Использование glob
Как объяснялось ранее, с Python 3.5+ это просто:
import glob
for f in glob.glob('d:/temp/**/*', recursive=True):
print(f)
#d:\temp\New folder
#d:\temp\New Text Document - Copy.txt
#d:\temp\New folder\New Text Document - Copy.txt
#d:\temp\New folder\New Text Document.txt
Использование pathlib
from pathlib import Path
for f in Path('d:/temp').glob('**/*'):
print(f)
Использование os.scandir
os.scandir
это что
glob
делает внутренне. Итак, вот как это сделать напрямую, используя
yield
:
def listpath(path):
for f in os.scandir(path):
f2 = os.path.join(path, f)
if os.path.isdir(f):
yield f2
yield from listpath(f2)
else:
yield f2
for f in listpath('d:\\temp'):
print(f)
Вот адаптированная версия, которая позволяет 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
сопоставление с образцом всего файла, а не только файла.
configfiles = glob.glob('C:/Users/sam/Desktop/**/*.txt")
Не работает во всех случаях, вместо этого используйте glob2
configfiles = glob2.glob('C:/Users/sam/Desktop/**/*.txt")
Если вы можете установить пакет 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\\**\\**")
Если вы используете Python 3.4+, вы можете использовать pathlib
модуль. Path.glob()
метод поддерживает **
шаблон, который означает "этот каталог и все подкаталоги, рекурсивно". Возвращает генератор, уступающий Path
объекты для всех соответствующих файлов.
from pathlib import Path
configfiles = Path("C:/Users/sam/Desktop/file1/").glob("**/*.txt")
Вы можете использовать функцию glob.glob()
или glob.iglob()
непосредственно из модуля glob для рекурсивного получения путей из каталогов / файлов и подкаталогов / подфайлов.
Синтаксис:
glob.glob(pathname, *, recursive=False) # pathname = '/path/to/the/directory' or subdirectory
glob.iglob(pathname, *, recursive=False)
В вашем примере можно написать так:
import glob
configfiles = [f for f in glob.glob("C:/Users/sam/Desktop/*.txt")]
for file in configfiles:
print(file)
Команда rglob
выполнит бесконечную рекурсию на самом глубоком подуровне вашей структуры каталогов. Однако, если вам нужен только один уровень, не используйте его.
Я понимаю, что OP говорил об использовании glob.glob. Однако я считаю, что это отвечает намерению, которое заключается в рекурсивном поиске всех подпапок.
В rglob
Функция недавно произвела 100-кратное увеличение скорости для алгоритма обработки данных, который использовал структуру папок в качестве фиксированного допущения для порядка чтения данных. Однако сrglob
мы смогли выполнить однократное сканирование всех файлов в указанном родительском каталоге или ниже, сохранить их имена в списке (более миллиона файлов), а затем использовать этот список, чтобы определить, какие файлы нам нужно открыть в любой момент в future, основанный только на соглашениях об именах файлов в зависимости от того, в какой папке они были.
Как отметил Мартейн, глоб может делать это только через **
Оператор введен в Python 3.5. Поскольку OP явно запрашивает модуль glob, следующее вернет ленивый итератор оценки, который ведет себя аналогично
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)
,