Come dividere un percorso dos nei suoi componenti in Python


154

Ho una variabile stringa che rappresenta un percorso dos es:

var = "d:\stuff\morestuff\furtherdown\THEFILE.txt"

Voglio dividere questa stringa in:

[ "d", "stuff", "morestuff", "furtherdown", "THEFILE.txt" ]

Ho provato a usare split()e replace()ma elaborano solo la prima barra rovesciata o inseriscono numeri esadecimali nella stringa.

Ho bisogno di convertire questa variabile di stringa in una stringa grezza in qualche modo in modo da poterla analizzare.

Qual'è il miglior modo per farlo?

Dovrei anche aggiungere che il contenuto di varie il percorso che sto cercando di analizzare, è in realtà il valore di ritorno di una query della riga di comando. Non sono i dati del percorso che mi genera. È memorizzato in un file e lo strumento da riga di comando non sfuggirà alle barre rovesciate.


6
Mentre rivedi queste risposte, ricorda che os.path.splitnon funziona perché non stai sfuggendo correttamente a quella stringa.
Jed Smith,

Devi scappare dalla stringa o usare rawstring: r"d:\stuff\morestuff\furtherdown\THEFILE.txt"per evitare cose come \sessere fraintesi.
smci

Risposte:


165

Sono stato morso un sacco di volte da persone che scrivono il proprio percorso giocherellando funzioni e sbagliare. Spazi, barre, barre rovesciate, due punti: le possibilità di confusione non sono infinite, ma gli errori possono essere facilmente commessi comunque. Quindi sono un pignolo per l'uso di os.path, e lo consiglio su tale base.

(Tuttavia, il percorso verso la virtù non è quello più facilmente intrapreso, e molte persone, quando lo trovano, sono tentate di prendere un sentiero scivoloso fino alla dannazione. Non si renderanno conto fino a quando un giorno tutto cadrà a pezzi, e loro - o , più probabilmente, qualcun altro - deve capire perché tutto è andato storto, e si scopre che qualcuno ha creato un nome file che mescola barre e barre rovesciate - e qualcuno suggerisce che la risposta è "non farlo". Don ' sia una di queste persone. Tranne quella che ha confuso barre e barre rovesciate - potresti essere loro se vuoi.)

È possibile ottenere l'unità e il percorso + file in questo modo:

drive, path_and_file = os.path.splitdrive(path)

Ottieni il percorso e il file:

path, file = os.path.split(path_and_file)

Ottenere i nomi delle singole cartelle non è particolarmente conveniente, ma è il tipo di onesto disagio medio che accresce il piacere di trovare in seguito qualcosa che effettivamente funziona bene:

folders = []
while 1:
    path, folder = os.path.split(path)

    if folder != "":
        folders.append(folder)
    else:
        if path != "":
            folders.append(path)

        break

folders.reverse()

(Questo appare "\"all'inizio foldersse il percorso era originariamente assoluto. Potresti perdere un po 'di codice se non lo volessi.)


@brone - Preferisco usare questa soluzione, piuttosto che dovermi scappare dalla barra rovesciata. Grazie!
BeeBand,

1
Sarei felice di essere smentito, ma mi sembra che la soluzione suggerita non funzioni se viene utilizzato un percorso come questo "C: \ usr \ rs0 \ my0 \ in111102.log" (a meno che l'input iniziale non sia una stringa non elaborata )?
shearichard,

1
Sembra che questo non divida correttamente un percorso se contiene solo una directory in OSX come "/ path / to / my / folder /", al fine di ottenere che si desideri aggiungere queste due righe all'inizio: if path.endswith("/"):e path = path[:-1].
Kevin London,

1
Preferisco la soluzione di @Tompa
jaycode il

1
Concordo con jaycode : la soluzione di Tompa è l' approccio canonico e avrebbe dovuto essere la risposta accettata. Questa alternativa eccessivamente complessa, inefficiente e soggetta a errori non riesce a raccogliere il codice di produzione. Non c'è motivo ragionevole per tentare (... e fallire, ovviamente) di analizzare iterativamente i nomi dei percorsi quando la semplice suddivisione delle stringhe ha successo con una sola riga di codice.
Cecil Curry,

287

farei

import os
path = os.path.normpath(path)
path.split(os.sep)

Innanzitutto normalizzare la stringa del percorso in una stringa corretta per il sistema operativo. Quindi os.sepdeve essere sicuro da usare come delimitatore nella suddivisione della funzione stringa.


25
L'unica vera risposta: è emersa . La soluzione canonica è la più semplice, ovviamente. Ecco! Perché è elegante e contumoso e non ha casi limite insopportabili.
Cecil Curry,

20
Come one-liner,os.path.normpath(a_path).split(os.path.sep)
Daniel Farrell,

2
Questo non sembra funzionare per path = root. In tal caso, il risultato di path.split è ['', '']. In effetti, in generale, questa soluzione split () fornisce una directory all'estrema sinistra con un nome di stringa vuota (che potrebbe essere sostituito dalla barra appropriata). Il problema principale è che una singola barra (avanti o indietro a seconda del sistema operativo) è il nome della directory principale, mentre altrove nel percorso è un separatore .
Gwideman,

2
Funzionerà meglio con un lstrip allora? os.path.normpath(path).lstrip(os.path.sep).split(os.path.sep)
Vidar,

1
@ user60561 Questo perché su Linux, la barra rovesciata è un carattere consentito nei nomi dei file, mentre su Windows una barra non lo è. Ecco perché su Windows normpathriconoscerà la barra in avanti come separatore. Su Linux, normpathsupponiamo semplicemente che tu abbia una directory chiamata \1\2e un file o una directory al suo interno chiamata 3.
Vojislav Stojkovic,

81

Puoi semplicemente usare l'approccio più Pythonic (IMHO):

import os

your_path = r"d:\stuff\morestuff\furtherdown\THEFILE.txt"
path_list = your_path.split(os.sep)
print path_list

Che ti darà:

['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

L'indizio qui è usare al os.sepposto di '\\'o '/', in quanto ciò rende il sistema indipendente.

Per rimuovere i due punti dalla lettera dell'unità (anche se non vedo alcun motivo per cui vorresti farlo), puoi scrivere:

path_list[0] = path_list[0][0]

22
Questo funziona some times. Altre volte (almeno su Windows) troverai percorsi che sembrano folder\folder2\folder3/file.txt. È meglio prima normalizzare (os.path.normpath) il percorso e quindi dividerlo.
Vikki,

7
Questa risposta era quasi arrivata. Come suggerisce Vikki , l'incapacità di normalizzare i nomi dei percorsi prima della divisione degli archi condanna gli incantesimi su casi limite comuni (ad es /foo//bar.). Vedi la risposta di Tompa per una soluzione più solida.
Cecil Curry,

62

In Python> = 3.4 questo è diventato molto più semplice. Ora puoi usare pathlib.Path.partsper ottenere tutte le parti di un percorso.

Esempio:

>>> from pathlib import Path
>>> Path('C:/path/to/file.txt').parts
('C:\\', 'path', 'to', 'file.txt')
>>> Path(r'C:\path\to\file.txt').parts
('C:\\', 'path', 'to', 'file.txt')

Su un'installazione Windows di Python 3 questo presupporrà che stai lavorando con percorsi Windows e su * nix supporrà che stai lavorando con percorsi posix. Questo è di solito quello che vuoi, ma se non lo è puoi usare le classi pathlib.PurePosixPatho pathlib.PureWindowsPathsecondo necessità:

>>> from pathlib import PurePosixPath, PureWindowsPath
>>> PurePosixPath('/path/to/file.txt').parts
('/', 'path', 'to', 'file.txt')
>>> PureWindowsPath(r'C:\path\to\file.txt').parts
('C:\\', 'path', 'to', 'file.txt')
>>> PureWindowsPath(r'\\host\share\path\to\file.txt').parts
('\\\\host\\share\\', 'path', 'to', 'file.txt')

Modifica: è disponibile anche un backport per Python 2: pathlib2


3
Path.parts è quello che ho sempre desiderato, ma non ho mai saputo che esistesse fino ad oggi.
JamEnergy,

perché questo non è stato racchiuso in una bella funzione nativa di Python?
Eduardo Pignatelli,

2
Questa è la risposta!
nayriz

11

Il problema qui inizia con come stai creando la stringa in primo luogo.

a = "d:\stuff\morestuff\furtherdown\THEFILE.txt"

Fatto questo modo, Python sta provando a caso speciale questi: \s, \m, \f, e \T. Nel tuo caso, \fviene trattato come un formfeed (0x0C) mentre le altre barre rovesciate vengono gestite correttamente. Quello che devi fare è uno di questi:

b = "d:\\stuff\\morestuff\\furtherdown\\THEFILE.txt"      # doubled backslashes
c = r"d:\stuff\morestuff\furtherdown\THEFILE.txt"         # raw string, no doubling necessary

Quindi, una volta divisi uno di questi, otterrai il risultato desiderato.


@W. Craig Trader - grazie, ma questo percorso non è quello che mi genera - mi ritorna da un altro programma e devo archiviare questi dati in una variabile. Non sono sicuro di come convertire i dati memorizzati in una variabile in "testo non elaborato".
BeeBand,

Non esiste un "testo non elaborato" ... è solo il modo in cui lo rappresenti nella fonte. O anteporre "" r alla stringa o passarla attraverso .replace ('\\', '/')
Marco Mariani,

@BeeBand, come stai recuperando i dati dall'altro programma? Lo stai leggendo da un file, una pipe, un socket? In tal caso, non è necessario fare nulla di speciale; l'unico motivo per raddoppiare le barre rovesciate o usare stringhe non elaborate è inserire le costanti di stringa nel codice Python. D'altra parte, se l'altro programma sta generando doppie barre rovesciate, allora vorrai ripulirlo prima di dividere il tuo percorso.
Craig Trader,

@W. Craig Trader - lo sto leggendo da un file, che viene scritto da un altro programma. Non sono riuscito a ottenere split()o replace()lavorare per qualche motivo - Continuavo a ottenere valori esadecimali. Hai ragione però, penso che stavo abbaiando l'albero sbagliato con l'idea di stringa grezza - penso che stavo solo usando in split()modo errato. Perché ho provato alcune di queste soluzioni usando split()e ora funzionano per me.
BeeBand,

10

Per una soluzione un po 'più concisa, considerare quanto segue:

def split_path(p):
    a,b = os.path.split(p)
    return (split_path(a) if len(a) and len(b) else []) + [b]

Questa è la mia soluzione preferita a questo problema. Molto bella.
Will Moore,

1
Questo non funziona se il percorso termina con /. Inoltre, ti dà una stringa vuota all'inizio dell'elenco se il tuo percorso inizia con/
Sorig

4

In realtà non posso dare una vera risposta a questa (dato che sono venuto qui sperando di trovarne una da solo), ma per me il numero di approcci diversi e tutti gli avvertimenti menzionati è l'indicatore più sicuro che il modulo os.path di Python ha un disperato bisogno di questo come funzione integrata.


4

Il modo funzionale, con un generatore .

def split(path):
    (drive, head) = os.path.splitdrive(path)
    while (head != os.sep):
        (head, tail) = os.path.split(head)
        yield tail

In azione:

>>> print([x for x in split(os.path.normpath('/path/to/filename'))])
['filename', 'to', 'path']

3

Per me funziona:

>>> a=r"d:\stuff\morestuff\furtherdown\THEFILE.txt"
>>> a.split("\\")
['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

Sicuramente potresti dover rimuovere anche i due punti dal primo componente, ma mantenerlo rende possibile riassemblare il percorso.

Il rmodificatore contrassegna la stringa letteralmente come "raw"; notare come le barre rovesciate integrate non siano raddoppiate.


@unwind - il rdavanti alla tua stringa, a cosa si riferisce?
BeeBand,

2
r significa stringa non elaborata: esegue l'escape automatico dei \ caratteri. È utile usarlo ogni volta che fai percorsi.
Wayne Werner,

1
@BeeBand: non devi preoccuparti; r "" è solo qualcosa che conta durante la compilazione / analisi del codice, non è qualcosa che diventa una proprietà della stringa una volta analizzata. Significa solo "ecco una stringa letterale, ma non interpretare le barre rovesciate come se non avessero altro significato che essere barre rovesciate".
Rilassati il

3
Penso che potrebbe essere utile menzionarti meno bene farlo in modo più ambiguo usando a.split (os.sep) invece di codificarlo duramente?
Tim McJilton,

4
Devo votarti per aver perso la possibilità di spiegare os.path.splite os.pathsep, considerando entrambi, sono molto più portabili di quello che hai scritto. Potrebbe non essere importante per OP adesso, ma lo farà quando sta scrivendo qualcosa che deve spostare piattaforme.
Jed Smith

3

Le cose su circa mypath.split("\\")sarebbero meglio espresse come mypath.split(os.sep). sepè il separatore di percorso per la tua particolare piattaforma (ad esempio, \per Windows, /per Unix, ecc.) e la build di Python sa quale utilizzare. Se lo usi sep, il tuo codice sarà indipendente dalla piattaforma.


1
Or os.path.split. Vuoi stare attento os.pathsep, perché è :sulla mia versione di Python in OS X (e os.path.splitgestisce correttamente /).
Jed Smith,

4
Intendi os.sepno os.pathsep. Segui la saggezza nei os.sepdocumenti: Nota che sapere questo non è sufficiente per essere in grado di analizzare o concatenare i nomi dei percorsi - usa os.path.split () e os.path.join ().
Jon-Eric,

1

re.split () può aiutare un po 'di più di string.split ()

import re    
var = "d:\stuff\morestuff\furtherdown\THEFILE.txt"
re.split( r'[\\/]', var )
['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

Se si desidera supportare anche i percorsi Linux e Mac, è sufficiente aggiungere un filtro (Nessuno, risultato), quindi rimuoverà lo "" indesiderato dalla divisione () poiché i loro percorsi iniziano con "/" o "//". ad esempio '// mount / ...' o '/ var / tmp /'

import re    
var = "/var/stuff/morestuff/furtherdown/THEFILE.txt"
result = re.split( r'[\\/]', var )
filter( None, result )
['var', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

1

È possibile ricorsivamente os.path.splitla stringa

import os
def parts(path):
    p,f = os.path.split(path)
    return parts(p) + [f] if f else [p]

Provando questo contro alcune stringhe di percorso e riassemblando il percorso con os.path.join

>>> for path in [
...         r'd:\stuff\morestuff\furtherdown\THEFILE.txt',
...         '/path/to/file.txt',
...         'relative/path/to/file.txt',
...         r'C:\path\to\file.txt',
...         r'\\host\share\path\to\file.txt',
...     ]:
...     print parts(path), os.path.join(*parts(path))
... 
['d:\\', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt'] d:\stuff\morestuff\furtherdown\THEFILE.txt
['/', 'path', 'to', 'file.txt'] /path\to\file.txt
['', 'relative', 'path', 'to', 'file.txt'] relative\path\to\file.txt
['C:\\', 'path', 'to', 'file.txt'] C:\path\to\file.txt
['\\\\', 'host', 'share', 'path', 'to', 'file.txt'] \\host\share\path\to\file.txt

Potrebbe essere necessario trattare il primo elemento dell'elenco in modo diverso a seconda di come si desidera gestire le lettere di unità, i percorsi UNC e i percorsi assoluti e relativi. La modifica dell'ultimo [p]per [os.path.splitdrive(p)]forzare il problema suddividendo la lettera di unità e la directory root in una tupla.

import os
def parts(path):
    p,f = os.path.split(path)
    return parts(p) + [f] if f else [os.path.splitdrive(p)]

[('d:', '\\'), 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']
[('', '/'), 'path', 'to', 'file.txt']
[('', ''), 'relative', 'path', 'to', 'file.txt']
[('C:', '\\'), 'path', 'to', 'file.txt']
[('', '\\\\'), 'host', 'share', 'path', 'to', 'file.txt']

Modifica: mi sono reso conto che questa risposta è molto simile a quella data sopra dal user1556435 . Lascio la mia risposta perché la gestione del componente di unità del percorso è diversa.


0

Proprio come altri hanno spiegato - il tuo problema derivava dall'uso \ , che è il carattere di escape nella stringa letterale / costante. OTOH, se avessi quella stringa del percorso del file da un'altra fonte (letta da file, console o restituita dalla funzione os) - non ci sarebbe stato problema a suddividere in '\\' o r '\'.

E proprio come altri hanno suggerito, se si desidera utilizzare \nel programma letterale, è necessario duplicarlo \\o l'intero letterale deve essere preceduto da r, in questo modo r'lite\ral'o r"lite\ral"per evitare che il parser lo converta \er in carattere CR (ritorno a capo).

C'è un altro modo, però: semplicemente non usare i \percorsi backslash nel tuo codice! Fin dal secolo scorso Windows riconosce e funziona bene con i nomi dei percorsi che usano la barra in avanti come separatore di directory /! In qualche modo non molte persone lo sanno ... ma funziona:

>>> var = "d:/stuff/morestuff/furtherdown/THEFILE.txt"
>>> var.split('/')
['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']

Questo a proposito farà funzionare il tuo codice su Unix, Windows e Mac ... perché tutti usano /come separatore di directory ... anche se non vuoi usare le costanti predefinite del modulo os.


Sfortunatamente i dati mi vengono restituiti da un altro programma che eseguo dal mio script Python. Non ho alcun controllo sull'opportunità di utilizzare '\' o '/' - è il programma di terze parti che determina questo (probabilmente su una piattaforma).
BeeBand,

@BeeBand: Ah, allora non avrai il problema che hai riscontrato durante i test, quando hai fornito la stringa come letterale nel tuo programma. Oppure puoi fare il seguente hack malefico dopo aver ricevuto il percorso: var = var.replace('\\','/')- sostituisci \ con / e continua a lavorare solo con le barre
rovesciate

questo è davvero un trucco malvagio: o)
BeeBand

@BeeBand: ecco perché l'ho avvertito. Quando dico qualcosa è male, io non intendo necessariamente non dovrebbe mai essere utilizzato - ma si dovrebbe molto essere a conoscenza perché lo si utilizza e vigile delle conseguenze non intenzionali. In questo caso, una conseguenza molto improbabile è che se questo viene utilizzato sul file system Unix con `` uso nel nome del file o della directory (è davvero difficile ma possibile) - questo codice si "spezzerà"
Nas Banov,

0

Supponiamo di avere un file filedata.txtcon contenuto:

d:\stuff\morestuff\furtherdown\THEFILE.txt
d:\otherstuff\something\otherfile.txt

Puoi leggere e dividere i percorsi dei file:

>>> for i in open("filedata.txt").readlines():
...     print i.strip().split("\\")
... 
['d:', 'stuff', 'morestuff', 'furtherdown', 'THEFILE.txt']
['d:', 'otherstuff', 'something', 'otherfile.txt']

questo funziona davvero, grazie! Ma ho scelto la soluzione di Brone perché preferisco non preoccuparmi di sfuggire alla barra rovesciata.
BeeBand,

9
Non pythonic poiché dipende dal filesystem.
jb.

0

Uso quanto segue poiché, poiché utilizza la funzione os.path.basename, non aggiunge barre all'elenco restituito. Funziona anche con le barre di qualsiasi piattaforma: ad esempio windows \\ o unix's /. Inoltre, non aggiunge il \\\\ che Windows utilizza per i percorsi del server :)

def SplitPath( split_path ):
    pathSplit_lst   = []
    while os.path.basename(split_path):
        pathSplit_lst.append( os.path.basename(split_path) )
        split_path = os.path.dirname(split_path)
    pathSplit_lst.reverse()
    return pathSplit_lst

Quindi per '\\\\ server \\ cartella1 \\ cartella2 \\ cartella3 \\ cartella4'

hai capito

[ 'Server', 'cartella1', 'folder2', 'Folder3', 'Folder4']


1
Ciò non segue l'invariante a cui passa il risultato os.path.join()dovrebbe restituire la stringa originale. Direi che l'output corretto per il tuo input di esempio è [r'\\','server','folder1','folder2','folder3','folder4']. Cioè cosa os.path.split()fa.
Jon-Eric,

0

In realtà non sono sicuro che questo risponda pienamente alla domanda, ma mi sono divertito a scrivere questa piccola funzione che mantiene uno stack, si attacca alle manipolazioni basate su os.path e restituisce l'elenco / stack di elementi.

  9 def components(path):
 10     ret = []
 11     while len(path) > 0:
 12         path, crust = split(path)
 13         ret.insert(0, crust)
 14
 15     return ret
 16

0

Sotto la riga di codice è possibile gestire:

  1. C: percorso / / path
  2. C: // // percorso percorso
  3. C: \ percorso \ percorso
  4. C: \ percorso \ percorso

path = re.split (r '[/// \]', percorso)


0

Uno ricorsivo per il divertimento.

Non è la risposta più elegante, ma dovrebbe funzionare ovunque:

import os

def split_path(path):
    head = os.path.dirname(path)
    tail = os.path.basename(path)
    if head == os.path.dirname(head):
        return [tail]
    return split_path(head) + [tail]

anzi, scusa. Avrei dovuto leggere attentamente la domanda ... un percorso "dos".
DuGNu,

-1

uso ntpath.split()


quando uso os.path.split () ottengo, ( d:\\stuff, morestuff\x0curtherdown\thefile.mux)
BeeBand

Come sottolineato da BeeBand, os.path.split () non fa davvero la cosa desiderata.
Rilassati il

scusate ho appena realizzato che os.path funziona solo in base al vostro sistema operativo. ntpath analizzerà dos percorsi.
deft_code

anche con ntpath ottengo ancorad:\\stuff, morestuff\x0curtherdown\thefile.mux
BeeBand

2
@BeeBand: hai problemi con l'escaping della stringa. '\x0c'è il carattere del feed del modulo. Il modo per creare il carattere di feed del modulo è '\ f'. Se vuoi davvero la stringa letterale '\ f' hai due opzioni: '\\f'o r'\f'.
deft_code
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.