【问题标题】:Most efficient way to find subdirectories paths查找子目录路径的最有效方法
【发布时间】:2020-02-21 22:27:09
【问题描述】:

假设我有以下目录结构

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 也是一个相当复杂的目录,有很多文件和子目录,这就是为什么递归搜索要花很多时间。

【问题讨论】:

  • 是否需要使用 glob.? ....我使用 os.walk

标签: python algorithm directory glob os.path


【解决方案1】:

如果我理解正确,您不想找到具有相同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

使用您的示例目录树,此函数返回:

{'.\\build',
 '.\\build\\sth1',
 '.\\build\\sth2',
 '.\\build\\sth2\\6.11.2',
 '.\\build\\sth2\\6.11.3',
 '.\\common'}

当然可以根据您是否要考虑除basename 之外的其他部分来修改此算法,但总体思路是执行traversal 并确定您的“已访问”标准是什么”。

编辑

由于我误解了问题,所以在下面添加答案:

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 目录中 6.11.2 目录的路径,如果我找到一个 ex。 PROJECT/COMMON/6.11.2我不必再深入,这意味着找到它后,我可以完全跳过搜索 COMMON 目录的其余部分
  • @TomaszHołda 在这种情况下更简单:我用它编辑了答案。思路是一样的:遍历,但是这一次找到想要的就添加到结果集,找不到就添加到队列中。
【解决方案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编写你自己的目录遍历。

【讨论】:

    猜你喜欢
    • 2020-05-07
    • 2019-12-14
    • 1970-01-01
    • 2019-05-26
    • 2010-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多