2012-01-13 13 views
5

Dostaję listę ścieżek, które muszę sprawdzić pliki wewnątrz. Oczywiście, jeśli dostaję root i podkatalog, nie ma potrzeby przetwarzania podkatalogu. Na przykład:Jak ustalić, czy ścieżka jest podkatalogiem innej?

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

Jak mogę stwierdzić (cross platform), że ścieżka nie jest podkatalogiem drugiej. Najlepiej byłoby, gdyby była to platforma krzyżowa i nie martwię się dowiązaniami symbolicznymi, o ile nie są one cykliczne (najgorszy przypadek polega na tym, że dwukrotnie przetwarzam dane).

UPDATE: Oto kod I skończył przy użyciu, dzięki @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) 

Odpowiedz

6

chciałbym utrzymać zestaw katalogów już przetworzone, a następnie dla każdej nowej ścieżki sprawdzić, czy któryś z jego rodzic katalogi istnieją już w tym zbiorze przed obróbką:

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) 

Zauważ, że path_list powinny być posortowane tak, aby podkatalogi są zawsze po ich katalogów nadrzędnych, jeśli takie istnieją.

+0

to będzie szybciej niż moją sugestią, ponieważ ma ustawiony testy członkostwa zamiast skanowania listy. Lubię to. – kindall

+0

@ F.J wydaje się być pętlą infitie, głowa redukuje się do c: \ w swojej bazie i nigdy nie zostaje ustawiona na Brak. – esac

+0

@esac - Przepraszam, myślałem, że w podstawowym przypadku wszystko włoży do ogona, a nie do głowy. Zobacz moją edycję, która powinna rozwiązać problem. –

2

Śledź już przetworzone katalogi (w znormalizowanym formularzu) i nie przetwarzaj ich ponownie, jeśli już je widziałeś. Coś jak to powinno działać:

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 
+1

'commonprefix ([r'C: \ test2 ', r'C: \ test']) -> 'C: \\ test'' –

+0

Tak, westchnienie. To naprawdę nie robi tego, co powinno, IMHO. Zmieniono go tak, aby zrobił proste 'startswith()' - to będzie w porządku, ponieważ jest znormalizowany. – kindall

+0

Tak, naprawdę uważam, że zachowanie commonprefix jest trochę dziwne, wygląda na to, że powinno się sprawdzać tylko w katalogach, ponieważ pochodzi z modułu 'os.path', no cóż. –

8
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 
+1

os.sep jest źródłem błędu :) –

+0

Użycie 'relpath' może zakończyć się niepowodzeniem w ms-windows, podczas próby znalezienia' C: \ foo' w stosunku do 'D: \ bar'. – ideasman42

-1

Stałe i uproszczone jgoeders „s wersja:

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) 
+0

Dlaczego po prostu nie edytuj postu, zamiast dodawać kolejną duplikat odpowiedzi? – jgoeders

+0

Przepraszam, zamieszczam raz w Wiśle w niebieskim księżycu i tęskniłem za tą funkcją. –

+0

'realpath' - wyeliminowanie tutaj dowiązań symbolicznych jest problematyczne. (w wielu przypadkach nie to, co chcesz), ponieważ poniższe linki mogą całkowicie zmienić układ ścieżki. – ideasman42

0

Oto funkcja is_subdir narzędzie wymyśliłem.

  • Python3.x kompatybilny (współpracuje z bytes i str, dopasowując os.path który obsługuje również zarówno).
  • Normalizuje ścieżki do porównania.
    (hierarchia nadrzędna i przypadek do pracy w ms-Windows).
  • Unika używania nazwy os.path.relpath, która spowoduje zgłoszenie wyjątku w ms-windows, jeśli ścieżki znajdują się na różnych dyskach. (C:\foo ->D:\bar)

Kod:

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 
Powiązane problemy