2014-11-03 15 views
15

Mam ścieżkę, która wygląda jakUsunięcie pierwszego folderu w ścieżce

/First/Second/Third/Fourth/Fifth 

i chciałbym, aby usunąć First z nim, a tym samym uzyskanie

Second/Third/Fourth/Fifth 

Jedyny pomysł mogę przyjść jest użycie rekursywnie os.path.split, ale nie wydaje się to optymalne. Czy istnieje lepsze rozwiązanie?

Odpowiedz

13

Naprawdę nie ma nic w module os.path, aby to zrobić. Co jakiś czas ktoś sugeruje utworzenie funkcji splitall, która zwraca listę (lub iteratora) wszystkich składników, ale nigdy nie uzyskała wystarczającej trakcji.

Częściowo dlatego, że za każdym razem, gdy ktoś kiedykolwiek sugerował dodanie nowej funkcjonalności do os.path, ponownie zapalił on długotrwałe niezadowolenie z ogólnego projektu biblioteki, co doprowadziło do tego, że ktoś zaproponował nowe, bardziej podobne do OO API dla Ścieżki do przestarzałych OS, clunky API. W 3.4, w końcu to się stało, z pathlib. I już ma funkcjonalność, która nie była w os.path. A więc:

>>> p = pathlib.Path('/First/Second/Third/Fourth/Fifth') 
>>> p.parts[2:] 
('Third', 'Fourth', 'Fifth') 
>>> pathlib.Path(*p.parts[2:]) 
PosixPath('Second/Third/Fourth/Fifth') 

Lub ... czy na pewno naprawdę chcesz usunąć pierwszy komponent, zamiast tego zrobić?

>>> p.relative_to(*p.parts[:2]) 
PosixPath('Second/Third/Fourth/Fifth') 

Jeśli trzeba to zrobić w 2,6-2,7 lub 3,2-3,3, tam backport of pathlib.

Oczywiście można użyć manipulacji ciągami, o ile zachowuje się ostrożność, aby znormalizować ścieżkę i użyć os.path.sep, oraz aby upewnić się, że obsługujesz skomplikowane szczegóły za pomocą ścieżek niesubtelnych lub systemów z literami dysków, a także ...

Albo możesz po prostu zamknąć rekursywną os.path.split. Czym dokładnie jest "nieoptymalny", gdy go podsumujesz? Może być nieco wolniej, ale mówimy tu o nanosekundach, o wiele rzędów wielkości szybciej niż nawet wywołując plik stat. Będzie miał problemy z głęboką rekurencją, jeśli masz system plików, który ma 1000 katalogów, ale czy kiedykolwiek widziałeś? (Jeśli tak, zawsze możesz przekształcić go w pętlę ...) Potrwa to kilka minut i zapisuje dobre testy jednostkowe, ale to jest coś, co robisz tylko raz i już nigdy się o to nie martwisz. Więc, szczerze mówiąc, jeśli nie chcesz używać pathlib, to właśnie bym zrobił.

+0

'pathlib' dosent pochodzą z python, trzeba go zainstalować – Hackaholic

+0

pod względem wydajności masz całkowitą rację: mówimy o nanosekundach; to bardziej ja próbuję nauczyć się najlepszego sposobu/innych sposobów robienia tego. – meto

+0

@Hackholmen: Jak wyjaśnia odpowiedź, 'pathlib' jest dostarczany z Pythonem 3.4+, i możesz zainstalować backport dla wersji 2.6-2.7 lub 3.2-3.3 . – abarnert

4

Proste podejście

a = '/First/Second/Third/Fourth/Fifth' 
"/".join(a.strip("/").split('/')[1:]) 

wyjściowa:

Second/Third/Fourth/Fifth 

W powyższym kodzie mam podzielić ciąg. Następnie dołączył pozostawiając 1-ej

Korzystanie itertools.dropwhile:

>>> a = '/First/Second/Third/Fourth/Fifth' 
>>> "".join(list(itertools.dropwhile(str.isalnum, a[1:]))[1:]) 
'Second/Third/Fourth/Fifth' 
+0

Początkowo myślałem, że to nie działa na ścieżkach, które zaczynają się od seperatora ścieżki, ponieważ wydaje się, że bezmyślnie usuwasz pierwszą literę ze sznurka, ale po dalszej analizie, co robi pierwsza postać, jeśli tylko usuwasz pierwszy segment . +1, ale może coś w odpowiedzi, która to mówi (a może komentarz kogoś, komu pomogłeś) – iLoveTux

+0

@iLoveTux sprawiło, że jest bardziej efektywny – Hackaholic

8

Trochę jak inna odpowiedź, korzystając z os.path:

os.path.join(*(x.split(os.path.sep)[2:])) 

... zakładając swój ciąg rozpoczyna się od separatora.

+1

Czy możesz wyjaśnić nieco o użyciu "*" tutaj? – Luke

+0

@Luke * stosuje się w celu traktowania zestawu generowanego przez '(x.split (os.path.sep) [2:])' jako słowo kluczowe '* args'. Jednak to nie zadziała, ponieważ ścieżka jest zbyt krótka, ponieważ lista argumentów będzie całkowicie pusta – asdf

1

Szukałem, czy jest to natywna metoda, ale wygląda na to, że tak nie jest.

Wiem, że ten temat jest stary, ale właśnie to zrobiłem, aby doprowadzić mnie do najlepszego rozwiązania: Było dwa zasadniczo dwa podejścia: używanie split() i używanie len(). Obaj musieli korzystać z krojenia.

1) za pomocą podzielonego()

import time 

start_time = time.time() 

path = "/folder1/folder2/folder3/file.zip" 
for i in xrange(500000): 
    new_path = "/" + "/".join(path.split("/")[2:]) 

print("--- %s seconds ---" % (time.time() - start_time)) 

Wynik: --- 0.420122861862 sekund ---

* Usuwanie karbonizatu "/" w new_path linii = "/" + "/".... nie poprawiło zbytnio wydajności.

2) Używanie len(). Ta metoda będzie działać tylko wtedy, gdy podasz folder, jeśli chcesz usunąć

import time 

start_time = time.time() 

path = "/folder1/folder2/folder3/file.zip" 
folder = "/folder1" 
for i in xrange(500000): 
    if path.startswith(folder): 
     a = path[len(folder):] 

print("--- %s seconds ---" % (time.time() - start_time)) 

Rezultat: --- 0.199596166611 sekund ---

* Mimo, że „jeśli”, aby sprawdzić jeśli ścieżka zaczyna się od nazwy pliku, była dwukrotnie szybsza niż pierwsza metoda.

Podsumowując: każda metoda ma pro i con. Jeśli jesteś absolutnie pewny co do folderu, który chcesz usunąć, użyj drugiej metody, w przeciwnym razie polecam użyć metody 1, którą ludzie tutaj wspomnieli wcześniej.

Powiązane problemy