Python:递归搜索包含扩展名为的文件的目录,找到文件时不包括子目录



下面是我的目录结构示例:https://pastebin.com/XimFQdS7

假设有数千个子目录和文件,递归搜索所有扩展名为"prj"的文件可能需要几秒钟时间。

假设我知道一个项目目录只包含一个"pjt"文件,我可以从搜索中丢弃它的所有子目录,从而节省大量时间。

这将是上述结构的期望输出:

[
'root/dir1/dirA/',
'root/dir1/dirB/',
'root/dir2/',
'root/dir3/dirA/dirX/',
'root/dir3/dirA/dirY/'
]

这是我当前的搜索代码:

def getSubDirectoriesContainingFileType(root, extension):
os.chdir(root)
fileFormat = '**/*.{}'.format(extension)
files = glob.glob(fileFormat, recursive = True)
matchingDirs = [os.path.dirname(os.path.abspath(file)) for file in files]
return matchingDirs

我用过glob,因为我发现它比os.walk((快一点,但我认为为了实现我上面所说的算法,我必须回到os.walk

该算法的思想是:

def searchDirs(root):
dirs = []
for dir in rootDirs:
search for file with ext
if found:
append dir to dirs
else:
append searchDirs(dir) to dirs
return dirs

虽然我可能会笨拙地实现这种算法,但我想知道是否有任何内置库已经提供了这种功能,以确保最大的效率。

我觉得你的问题很有趣,所以我做了一些测试。比较glob中的原始结果,而不过滤不需要的子目录,并使用特定的搜索功能搜索所需结果

特定的搜索速度似乎是的两倍,可以在不需要任何进一步处理的情况下得到所需的结果。

我认为诀窍在于

1如果发现prj文件,则立即停止循环

2在同一循环中同时搜索子目录和prj文件。

import os
import random
from pathlib import Path
import time
# --------------------------------------------------------
def make_sub_dirs(dir):
new_dir_paths = []
for i in range(4):
new_dir_path = os.path.join(dir, f'dir{i}')
os.makedirs(new_dir_path)
if random.randint(0, 3) == 1:
prj_file = os.path.join(new_dir_path, 'test.prj')
Path(prj_file).touch()
new_dir_paths.append(new_dir_path)
return new_dir_paths
# --------------------------------------------------------
def make_tree():
base_dir = [r'K:test_tree']
if os.path.exists(base_dir[0]):
# already made
return
current_level_dir_paths = base_dir
for depth in range(3):
new_dir_paths = []
for dir in current_level_dir_paths:
new_dir_paths.extend(make_sub_dirs(dir))
current_level_dir_paths = new_dir_paths
# --------------------------------------------------------
def search_first_prj_dirs(search_dir_path):
dirs_found = []
dir_entries = os.listdir(search_dir_path)
for dir_entry in dir_entries:
dir_entry_path = os.path.join(search_dir_path, dir_entry)
if os.path.isfile(dir_entry_path):
if os.path.splitext(dir_entry)[-1] == '.prj':
# found !!!!!!
return [search_dir_path]
else:
# this should be a dir
dirs_found.append(dir_entry_path)
# no prj found, recurse into the found sub dirs
first_prj_dirs = []
for dir in dirs_found:
first_prj_dirs.extend(search_first_prj_dirs(dir))
return first_prj_dirs
# --------------------------------------------------------
make_tree()
start_time = time.time()
raw_glob_result = list(Path(r'K:test_tree').rglob('*.prj'))
end_time = time.time()
print('raw glob', len(raw_glob_result), end_time - start_time)
start_time = time.time()
search_first_prj_dirs_result = search_first_prj_dirs(r'K:test_tree')
end_time = time.time()
print('specific', len(search_first_prj_dirs_result), end_time - start_time)
print('-------------')
raw_glob_result.sort()
for result in raw_glob_result:
print(result)
print('-------------')
search_first_prj_dirs_result.sort()
for result in search_first_prj_dirs_result:
print(result)

结果

raw glob 17 0.023003816604614258
specific 11 0.010011434555053711
------------- raw glob
K:test_treedir0dir0dir0test.prj
K:test_treedir0dir3dir2test.prj
K:test_treedir1dir0dir2test.prj
K:test_treedir1dir0test.prj
K:test_treedir1dir2dir1test.prj
K:test_treedir1dir2dir3test.prj
K:test_treedir1dir3dir0test.prj
K:test_treedir1dir3dir1test.prj
K:test_treedir1dir3test.prj
K:test_treedir2dir0dir2test.prj
K:test_treedir2dir1dir1test.prj
K:test_treedir2dir2dir1test.prj
K:test_treedir2dir3dir2test.prj
K:test_treedir3dir1dir1test.prj
K:test_treedir3dir2dir3test.prj
K:test_treedir3dir3test.prj
K:test_treedir3test.prj
------------- specific
K:test_treedir0dir0dir0
K:test_treedir0dir3dir2
K:test_treedir1dir0
K:test_treedir1dir2dir1
K:test_treedir1dir2dir3
K:test_treedir1dir3
K:test_treedir2dir0dir2
K:test_treedir2dir1dir1
K:test_treedir2dir2dir1
K:test_treedir2dir3dir2
K:test_treedir3

使用Path.rglob

from pathlib import Path
def searchDirs(root):
return list(Path(root).rglob('*.prj'))

输出:

/root/dir1/dirB/project.prj
/root/dir1/dirB/test/project.prj
/root/dir1/diraA/project.prj
/root/dir3/dirx/project.prj
/root/dir2/project.prj

最新更新