2014-11-03 12 views
15

Ho un percorso che assomigliarimozione della prima cartella in un percorso

/First/Second/Third/Fourth/Fifth 

e vorrei rimuovere il First da esso, ottenendo così

Second/Third/Fourth/Fifth 

L'unica idea che potevo venire con è usare ricorsivamente os.path.split ma questo non sembra ottimale. C'è una soluzione migliore?

risposta

13

Non c'è davvero nulla nel modulo os.path per farlo. Ogni tanto qualcuno suggerisce di creare una funzione splitall che restituisce una lista (o iteratore) di tutti i componenti, ma non ha mai ottenuto una trazione sufficiente. In parte questo perché ogni volta che qualcuno ha mai suggerito di aggiungere nuove funzionalità a os.path, ha riacceso l'insoddisfazione di vecchia data con il progetto generale della libreria, portando a qualcuno che propone una nuova API più simile a OO per percorsi a deprecato dell'OS, clunky API. Nel 3.4, finalmente è successo, con pathlib. E ha già funzionalità che non erano in os.path. Quindi:

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

Oppure ... sei sicuro di voler veramente rimuovere il primo componente, piuttosto che farlo?

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

Se avete bisogno di fare questo in 2,6-2,7 o 3,2-3,3, c'è un backport of pathlib.

Naturalmente, è possibile utilizzare la manipolazione di stringhe, fino a quando si sta attenti a normalizzare il percorso e utilizzare os.path.sep, e per essere sicuri di gestire i dettagli poco pratici con percorsi non assoluti o con sistemi con lettere di unità, e ...

Oppure puoi semplicemente concludere il tuo ricorsivo os.path.split. Che cosa è esattamente "non ottimale" a riguardo, una volta concluso? Potrebbe essere un po 'più lento, ma stiamo parlando di nanosecondi qui, molti ordini di grandezza più veloce di persino chiamare stat su un file. Avrà problemi di profondità della ricorsione se hai un filesystem con 1000 directory profonde, ma ne hai mai visto uno? (Se è così, puoi sempre trasformarlo in un loop ...) Ci vogliono alcuni minuti per avvolgerlo e scrivere buoni test unitari, ma è qualcosa che devi fare una sola volta e non preoccuparti mai più. Quindi, onestamente, se non vuoi usare pathlib, è quello che farei.

+0

'pathlib' dosent fornito con python, è necessario installarlo – Hackaholic

+0

per quanto riguarda le prestazioni avete perfettamente ragione: stiamo parlando di nanosecondi; è più che sto cercando di imparare il modo migliore/altri modi per farlo – meto

+0

@Hackaholic: come la risposta spiega in dettaglio, 'pathlib' viene fornito con Python 3.4+, ed è possibile installare il backport per 2.6-2.7 o 3.2-3.3 . – abarnert

4

Un semplice approccio

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

uscita:

Second/Third/Fourth/Fifth 

In questo codice sopra ho dividere la stringa. poi uniti lasciando 1 ° elemento

Utilizzando itertools.dropwhile:

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

All'inizio pensavo che non avrebbe funzionato su percorsi che iniziano con il separatore di percorso perché sembra che vogliano spogliare il primo carattere dalla stringa, ma dopo un'ulteriore revisione, che importanza ha il primo personaggio se si rimuove solo il primo segmento . +1, ma forse qualcosa nella risposta che dice questo (o forse un commento da qualcuno che hai aiutato) – iLoveTux

+0

@iLoveTux lo ha reso più efficiente – Hackaholic

8

Un po 'come un'altra risposta, approfittando della os.path:

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

... supponendo che la stringa inizia con un separatore.

+1

Puoi spiegare un po 'sull'uso del "*" qui? – Luke

+0

@Luke Il * è usato per trattare l'insieme generato da '(x.split (os.path.sep) [2:])' come la parola chiave '* args'. Tuttavia, questo non funzionerà è il percorso è troppo breve poiché l'elenco degli argomenti sarà completamente vuoto – asdf

1

Stavo cercando se esistesse un modo nativo per farlo, ma sembra che non sia così.

So che questo argomento è vecchio, ma questo è quello che ho fatto per ottenere la soluzione migliore: Esistevano due approcci fondamentalmente due: utilizzando split() e usando len() Entrambi dovevano usare l'affettatura.

1) Utilizzando Split()

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

Risultato: --- --- 0.420122861862 secondi

* Rimozione del carattere "/" nella riga new_path = "/" + "/".... non ha migliorato troppo le prestazioni.

2) Utilizzo di len(). Questo metodo funziona solo se si fornisce la cartella se si desidera rimuovere

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

Risultato: --- --- 0.199596166611 secondi

* Anche con quel "se" per controllare se il percorso inizia con il nome del file, era due volte più veloce del primo metodo.

In sintesi: ogni metodo ha un pro e un con. Se sei assolutamente sicuro della cartella che vuoi rimuovere usa il metodo due, altrimenti ti consiglio di usare il metodo 1 che le persone qui hanno menzionato in precedenza.

Problemi correlati