Как определить, является ли путь подкаталогом другого? - PullRequest
6 голосов
/ 13 января 2012

Мне дан список путей, по которым мне нужно проверить файлы.Конечно, если мне дают рут и подкаталог, нет необходимости обрабатывать подкаталог.Например,

c:\test  // process this
c:\test\pics // do not process this
c:\test2 // process this

Как я могу сказать (кросс-платформенный), что путь не является подкаталогом другого.Желательно, чтобы это было кроссплатформенным, и я не беспокоюсь о символических ссылках, если они не цикличны (в худшем случае я заканчиваю обработку данных дважды).

UPDATE: вот кодв конечном итоге использовать, благодаря @ FJ

   def unique_path_roots(paths):
    visited = set()
    paths = list(set(paths))

    for path in sorted(paths,key=cmp_to_key(locale.strcoll)):
        path = normcase(normpath(realpath(path)))

        head, tail = os.path.split(path)
        while head and tail:
            if head in visited:
                break
            head, tail = os.path.split(head)
        else:
            yield path
            visited.add(path)

Ответы [ 5 ]

8 голосов
/ 08 августа 2013
def is_subdir(path, directory):
    path = os.path.realpath(path)
    directory = os.path.realpath(directory)

    relative = os.path.relpath(path, directory)

    if relative.startswith(os.pardir):
        return False
    else:
        return True
7 голосов
/ 13 января 2012

Я бы сохранил набор каталогов, которые вы уже обработали, и затем для каждого нового пути проверяйте, существует ли какой-либо из его родительских каталогов в этом наборе до обработки:

import os.path

visited = set()
for path in path_list:
    head, tail = os.path.split(path)
    while head and tail:
        if head in visited:
            break
        head, tail = os.path.split(head)
    else:
        process(path)
        visited.add(path)

Обратите внимание, что path_list следует отсортировать так, чтобы подкаталоги всегда были после их родительских каталогов, если они существуют.

2 голосов
/ 13 января 2012

Отслеживайте каталоги, которые вы уже обработали (в нормализованной форме), и не обрабатывайте их снова, если вы уже видели их.Примерно так должно работать:

from os.path import realpath, normcase, sep

dirs = [r"C:\test", r"C:\test\pics", r"C:\test2"]

processed = []

for dir in dirs:
    dir = normcase(realpath(dir)) + sep
    if not any(dir.startswith(p) for p in processed):
        processed.append(dir)
        process(dir)            # your code here
0 голосов
/ 26 марта 2015

Вот полезная функция is_subdir, которую я придумал.

  • Python3.x совместимый (работает с bytes и str, соответствует os.path, который также поддерживает оба).
  • Нормализует пути для сравнения.
    (родительская иерархия и регистр для работы в ms-windows).
  • Избегает использования os.path.relpath, что вызовет исключение в ms-windows, если пути находятся на разных дисках. (C:\foo -> D:\bar)

Код:

def is_subdir(path, directory):
    """
    Returns true if *path* in a subdirectory of *directory*.
    """
    import os
    from os.path import normpath, normcase, sep
    path = normpath(normcase(path))
    directory = normpath(normcase(directory))
    if len(path) > len(directory):
        sep = sep.encode('ascii') if isinstance(directory, bytes) else sep
        if path.startswith(directory.rstrip(sep) + sep):
            return True
    return False
0 голосов
/ 29 апреля 2014

Исправлено и упрощено * Версия 1001 * jgoeders :

def is_subdir(suspect_child, suspect_parent):
    suspect_child = os.path.realpath(suspect_child)
    suspect_parent = os.path.realpath(suspect_parent)

    relative = os.path.relpath(suspect_child, start=suspect_parent)

    return not relative.startswith(os.pardir)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...