查找子目录路径的最有效方法
Most efficient way to find subdirectories paths
假设我有以下目录结构
PROJECT
|
+ BUILD
| |
| + STH1
| | |
| | + 6.11.2
| | |
| | + 6.11.3
| | |
| | + .....
| + STH2
| | |
| | + 6.11.2
| | |
| | + 6.11.3
| | |
| | + .....
+ COMMON
| |
| + 6.11.2
| |
| + ....
在 python 的 PROJECT 目录中找到所有 6.11.2 目录的最有效方法是什么?
我试过使用
glob.glob('PROJECT/**/6.11.2', recursive=True)
它有效,但对我来说不是最好的解决方案,因为它还会查找像
这样的目录
PROJECT/BUILD/STH1/6.11.2/6.11.2
所以要花很多时间,我只需要找到目录6.11.2的第一个出现,所以深入搜索是浪费时间(还有像sth/6.11.2/这样的情况6.11.2不会出现在我的结构中)
在 python 中是否有更好的搜索方式?
请注意,6.11.2 也是一个非常复杂的目录,包含大量文件和子目录,这就是为什么递归搜索需要花费大量时间的原因。
缓慢的原因可能是匹配的文件多于嵌套的 6.11.2
目录。查看将 /
附加到您的模式是否有助于忽略文件。
此外,尝试使用迭代器版本 iglob,这将节省 glob
将所有内容放入列表的成本。
filter(lambda p: p.count('6.11.2') == 1, glob.iglob('PROJECT/**/6.11.2/', recursive=True))
否则你总是可以用os.scandir编写你自己的目录遍历。
如果我没理解错的话,你不想找basename相同的目录,是吗?如果是,那么这应该可以解决问题:
import os
from collections import deque
from typing import List, Set
def scandir_only_dirs(path: str) -> List[str]:
return [f.path for f in os.scandir(path) if f.is_dir()]
def scandir_no_same_basename(path: str) -> Set[str]:
result = set()
queue = deque(scandir_only_dirs(path))
if not queue:
return result
visited_basenames = set()
while queue:
currdir = queue.popleft()
basename = os.path.basename(currdir)
if basename not in visited_basenames:
result.add(currdir)
queue.extendleft(scandir_only_dirs(currdir))
visited_basenames.add(basename)
return result
使用您的示例目录树,此函数 returns:
{'.\build',
'.\build\sth1',
'.\build\sth2',
'.\build\sth2\6.11.2',
'.\build\sth2\6.11.3',
'.\common'}
当然可以根据您是否要考虑 basename
以外的其他部分来修改此算法,但总体思路是执行 traversal 并建立什么是 "visited".
的标准
编辑
添加下面的答案是因为我误解了问题:
def find_paths_to_dir(dir_basename: str, from_path: str=".") -> Set[str]:
result = set()
queue = deque(scandir_only_dirs(from_path))
if not queue:
return result
while queue:
currdir = queue.popleft()
basename = os.path.basename(currdir)
if basename == dir_basename:
result.add(currdir)
else:
queue.extendleft(scandir_only_dirs(currdir))
return result
假设我有以下目录结构
PROJECT
|
+ BUILD
| |
| + STH1
| | |
| | + 6.11.2
| | |
| | + 6.11.3
| | |
| | + .....
| + STH2
| | |
| | + 6.11.2
| | |
| | + 6.11.3
| | |
| | + .....
+ COMMON
| |
| + 6.11.2
| |
| + ....
在 python 的 PROJECT 目录中找到所有 6.11.2 目录的最有效方法是什么?
我试过使用
glob.glob('PROJECT/**/6.11.2', recursive=True)
它有效,但对我来说不是最好的解决方案,因为它还会查找像
这样的目录PROJECT/BUILD/STH1/6.11.2/6.11.2
所以要花很多时间,我只需要找到目录6.11.2的第一个出现,所以深入搜索是浪费时间(还有像sth/6.11.2/这样的情况6.11.2不会出现在我的结构中)
在 python 中是否有更好的搜索方式?
请注意,6.11.2 也是一个非常复杂的目录,包含大量文件和子目录,这就是为什么递归搜索需要花费大量时间的原因。
缓慢的原因可能是匹配的文件多于嵌套的 6.11.2
目录。查看将 /
附加到您的模式是否有助于忽略文件。
此外,尝试使用迭代器版本 iglob,这将节省 glob
将所有内容放入列表的成本。
filter(lambda p: p.count('6.11.2') == 1, glob.iglob('PROJECT/**/6.11.2/', recursive=True))
否则你总是可以用os.scandir编写你自己的目录遍历。
如果我没理解错的话,你不想找basename相同的目录,是吗?如果是,那么这应该可以解决问题:
import os
from collections import deque
from typing import List, Set
def scandir_only_dirs(path: str) -> List[str]:
return [f.path for f in os.scandir(path) if f.is_dir()]
def scandir_no_same_basename(path: str) -> Set[str]:
result = set()
queue = deque(scandir_only_dirs(path))
if not queue:
return result
visited_basenames = set()
while queue:
currdir = queue.popleft()
basename = os.path.basename(currdir)
if basename not in visited_basenames:
result.add(currdir)
queue.extendleft(scandir_only_dirs(currdir))
visited_basenames.add(basename)
return result
使用您的示例目录树,此函数 returns:
{'.\build',
'.\build\sth1',
'.\build\sth2',
'.\build\sth2\6.11.2',
'.\build\sth2\6.11.3',
'.\common'}
当然可以根据您是否要考虑 basename
以外的其他部分来修改此算法,但总体思路是执行 traversal 并建立什么是 "visited".
编辑
添加下面的答案是因为我误解了问题:
def find_paths_to_dir(dir_basename: str, from_path: str=".") -> Set[str]:
result = set()
queue = deque(scandir_only_dirs(from_path))
if not queue:
return result
while queue:
currdir = queue.popleft()
basename = os.path.basename(currdir)
if basename == dir_basename:
result.add(currdir)
else:
queue.extendleft(scandir_only_dirs(currdir))
return result