Estrai il nome del file dal percorso, indipendentemente dal formato os / percorso


794

Quale libreria Python posso usare per estrarre i nomi dei file dai percorsi, indipendentemente dal sistema operativo o dal formato del percorso?

Ad esempio, vorrei che tutti questi percorsi mi restituissero c:

a/b/c/
a/b/c
\a\b\c
\a\b\c\
a\b\c
a/b/../../a/b/c/
a/b/../../a/b/c

Risposte:


781

L'uso os.path.splito os.path.basenamecome altri suggeriscono non funzionerà in tutti i casi: se si esegue lo script su Linux e si tenta di elaborare un percorso classico in stile Windows, non funzionerà.

I percorsi di Windows possono utilizzare la barra rovesciata o la barra in avanti come separatore di percorso. Pertanto, il ntpathmodulo (che equivale a os.path quando è in esecuzione su Windows) funzionerà per tutti (1) i percorsi su tutte le piattaforme.

import ntpath
ntpath.basename("a/b/c")

Naturalmente, se il file termina con una barra, il nome di base sarà vuoto, quindi crea la tua funzione per gestirlo:

def path_leaf(path):
    head, tail = ntpath.split(path)
    return tail or ntpath.basename(head)

Verifica:

>>> paths = ['a/b/c/', 'a/b/c', '\\a\\b\\c', '\\a\\b\\c\\', 'a\\b\\c', 
...     'a/b/../../a/b/c/', 'a/b/../../a/b/c']
>>> [path_leaf(path) for path in paths]
['c', 'c', 'c', 'c', 'c', 'c', 'c']


(1) C'è un avvertimento: i nomi di file Linux possono contenere barre rovesciate . Quindi su Linux, r'a/b\c'si riferisce sempre al file b\cnella acartella, mentre su Windows, si riferisce sempre al cfile nella bsottocartella della acartella. Pertanto, quando vengono utilizzate barre sia in avanti che all'indietro in un percorso, è necessario conoscere la piattaforma associata per poterla interpretare correttamente. In pratica, di solito è sicuro supporre che sia un percorso di Windows poiché le barre rovesciate sono usate raramente nei nomi di file Linux, ma tienilo a mente quando scrivi il codice in modo da non creare falle di sicurezza accidentali.


29
su Windows, os.pathcarica semplicemente il ntpathmodulo internamente. Utilizzando questo modulo, è possibile gestire i '\\'separatori di percorso anche su macchine Linux. Per Linux il posixpathmodulo (resp. os.path) Semplificherà le operazioni del percorso per consentire solo i '/'separatori di stile posix .
Moooeeeep,

@moooeeeep Quindi potremmo usare la risposta di Stranac, ed è affidabile? ( "L'uso di os.path.split o os.path.basename come altri suggeriscono non funzionerà in tutti i casi: se stai eseguendo lo script su Linux e cerchi di elaborare un classico percorso in stile Windows, fallirà" - - la citazione è tratta dal post di Lauritz - e non capisco, questo avvertimento riguarda la risposta di Stranac o no).
john cj,

3
@ johnc.j. Solo quando è necessario analizzare i percorsi di stile Windows (ad es. r'C:\path\to\file.txt') Su una macchina Linux, è necessario utilizzare il modulo ntpath. Altrimenti, è possibile utilizzare le funzioni da os.path. Questo perché i sistemi Linux normalmente consentono l'uso dei caratteri backslash nei nomi dei file (come spiegato nella risposta).
Moooeeeep,

2
La tua soluzione non è equivalente a os.path.basename(os.path.normpath(path))?
Mr_and_Mrs_D,

2
Per quello che vale per i futuri visitatori di questa domanda, mi sono imbattuto nella situazione di cui Lauritz stava avvertendo e la sua soluzione era l'unica che ha funzionato. Nessun finangling con os potrebbe generare solo il nome del file. Quindi imho, ntpath è la strada da percorrere.
Harabeck,

1250

In realtà, c'è una funzione che restituisce esattamente quello che vuoi

import os
print(os.path.basename(your_path))

22
Se si desidera elaborare i percorsi in modo indipendente dal SO, quindi per os.path.basename (u "C: \\ temp \\ bla.txt") ci si aspetta di ottenere 'bla.txt'. La domanda non riguarda l'ottenimento di un nome file valido, ma l'estrazione del nome per un percorso.
Adi Roiban,

3
Nella mia ricerca su Google per trovare il nome file di un percorso, questa risposta è stata la più utile. Il mio caso d'uso è comunque solo su Windows.
Bobort,

2
os.path.basename(your_path)Questo ha funzionato! Volevo percorso dello script: os.path.dirname(os.path.realpath(__file__))e il nome script: os.path.basename(os.path.realpath(__file__)). Grazie!
TheWalkingData,

@AdiRoiban Potresti per favore elaborare il tuo commento? L'ho provato su Windows 7 e in realtà ottengo "bla.txt '. Semplicemente dicendo, non vedo alcun problema (per me stesso).
john cj

10
@ johnc.j. Il punto è che quando si tenta di farlo su Linux, si ottiene 'C:\\temp\\bla.txt'invece.
Moooeeeep,

218

os.path.split è la funzione che stai cercando

head, tail = os.path.split("/tmp/d/a.dat")

>>> print(tail)
a.dat
>>> print(head)
/tmp/d

40
Solo per gli altri utenti di fare attenzione, questo restituisce "" se i percorsi terminano in "/" o "\"
BuZz

Quando provo "C: \ Users \ Dell \ Desktop \ ProjectShadow \ button \ button.py" restituisce il "ProjectShadow utton tton" per tutto ciò che non restituisce il risultato corretto
amitnair92

4
@ amitnair92 - O fare questo: r "C: \ Users \ Dell \ Desktop \ ProjectShadow \ button \ button.py" oppure questo: "C: \\ Users \\ Dell \\ Desktop \\ ProjectShadow \\ button \\ button .py "-" \ b "è un carattere speciale (credo che la 'campana' del sistema) sia simile a come \ r o \ n significano ritorno a capo / a capo. Il prefisso della stringa con r "C: \ ..." significa utilizzare l'input non
elaborato specificato

87

In Python 3

>>> from pathlib import Path    
>>> Path("/tmp/d/a.dat").name
'a.dat'

Da 3.4 a 3.6 o successive, a seconda di quali elementi pathlib si utilizzano.
LightCC,

8
può anche usare Path ("some / path / to / file.dat"). stem per ottenere il nome file senza l'estensione del file
s2t2

47
import os
head, tail = os.path.split('path/to/file.exe')

tail è quello che vuoi, il nome del file.

Vedi i documenti del modulo OS Python per i dettagli


13
Solo per gli altri utenti di fare attenzione, questo restituisce "" se i percorsi terminano in "/" o "\"
BuZz

19
import os
file_location = '/srv/volume1/data/eds/eds_report.csv'
file_name = os.path.basename(file_location )  #eds_report.csv
location = os.path.dirname(file_location )    #/srv/volume1/data/eds

12

Nel tuo esempio dovrai anche rimuovere la barra da destra a destra per tornare c:

>>> import os
>>> path = 'a/b/c/'
>>> path = path.rstrip(os.sep) # strip the slash from the right side
>>> os.path.basename(path)
'c'

Secondo livello:

>>> os.path.filename(os.path.dirname(path))
'b'

aggiornamento: penso che lazyrabbia fornito la risposta giusta. Il mio codice non funzionerà con percorsi simil-windows su sistemi unix e viceversa con percorsi simil-unix su sistemi windows.


La tua risposta non funzionerà per r"a\b\c"Linux, né per "a/b/c"Windows.
Lauritz V. Thaulow,

ovviamente os.path.basename(path)funzionerà solo se lo os.path.isfile(path)è True. Pertanto path = 'a/b/c/'non è affatto un nome file valido ...
moooeeeep

1
@fmaas os.path.basename è puramente una funzione di elaborazione delle stringhe. Non importa se il file esiste o se si tratta di un file o dir. os.path.basename("a/b/c/")ritorna a ""causa della barra finale.
Lauritz V. Thaulow,

lazyrhai ragione! Non ci ho pensato. Sarebbe sicuro farlo path = path.replace('\\', '/')?
Sci,

@Skirmantas Suppongo, ma non sembra giusto. Penso che l'elaborazione del percorso debba essere eseguita con gli strumenti integrati creati per il lavoro. C'è molto di più nei percorsi di quanto non sembri.
Lauritz V. Thaulow,

11
fname = str("C:\Windows\paint.exe").split('\\')[-1:][0]

questo restituirà: paint.exe

cambia il valore sep della funzione split relativa al tuo percorso o sistema operativo.


Questa è la risposta che mi è piaciuta, ma perché non fare semplicemente quanto segue? fname = str(path).split('/')[-1]
asultan904

10

Se vuoi ottenere automaticamente il nome file, puoi farlo

import glob

for f in glob.glob('/your/path/*'):
    print(os.path.split(f)[-1])

8

Se il percorso del file non termina con "/" e le directory separate da "/", utilizzare il codice seguente. Come sappiamo generalmente il percorso non termina con "/".

import os
path_str = "/var/www/index.html"
print(os.path.basename(path_str))

Ma in alcuni casi come gli URL terminano con "/" quindi utilizzare il codice seguente

import os
path_str = "/home/some_str/last_str/"
split_path = path_str.rsplit("/",1)
print(os.path.basename(split_path[0]))

ma quando il tuo percorso è sperato da "\" che trovi generalmente nei percorsi di Windows, puoi usare i seguenti codici

import os
path_str = "c:\\var\www\index.html"
print(os.path.basename(path_str))

import os
path_str = "c:\\home\some_str\last_str\\"
split_path = path_str.rsplit("\\",1)
print(os.path.basename(split_path[0]))

È possibile combinare entrambi in una funzione controllando il tipo di sistema operativo e restituendo il risultato.


7

Funziona anche con Linux e Windows con la libreria standard

paths = ['a/b/c/', 'a/b/c', '\\a\\b\\c', '\\a\\b\\c\\', 'a\\b\\c',
         'a/b/../../a/b/c/', 'a/b/../../a/b/c']

def path_leaf(path):
    return path.strip('/').strip('\\').split('/')[-1].split('\\')[-1]

[path_leaf(path) for path in paths]

risultati:

['c', 'c', 'c', 'c', 'c', 'c', 'c']

6

Ecco una soluzione solo per regex, che sembra funzionare con qualsiasi percorso del sistema operativo su qualsiasi sistema operativo.

Non è necessario nessun altro modulo e non è necessaria nemmeno la preelaborazione:

import re

def extract_basename(path):
  """Extracts basename of a given path. Should Work with any OS Path on any OS"""
  basename = re.search(r'[^\\/]+(?=[\\/]?$)', path)
  if basename:
    return basename.group(0)


paths = ['a/b/c/', 'a/b/c', '\\a\\b\\c', '\\a\\b\\c\\', 'a\\b\\c',
         'a/b/../../a/b/c/', 'a/b/../../a/b/c']

print([extract_basename(path) for path in paths])
# ['c', 'c', 'c', 'c', 'c', 'c', 'c']


extra_paths = ['C:\\', 'alone', '/a/space in filename', 'C:\\multi\nline']

print([extract_basename(path) for path in extra_paths])
# ['C:', 'alone', 'space in filename', 'multi\nline']

Aggiornare:

Se si desidera solo un potenziale nome di file, se presente (vale a dire, /a/b/è una directory e così è c:\windows\), modificare la regex per: r'[^\\/]+(?![\\/])$'. Per il "regex sfidato", questo cambia lo sguardo in avanti positivo per una sorta di barra in uno sguardo in avanti negativo, facendo sì che i nomi di percorso che terminano con tale barra non restituiscano nulla invece dell'ultima sottodirectory nel percorso. Naturalmente non vi è alcuna garanzia che il nome del file potenziale si riferisca effettivamente a un file e per questo os.path.is_dir()o os.path.is_file()dovrebbe essere impiegato.

Questo corrisponderà come segue:

/a/b/c/             # nothing, pathname ends with the dir 'c'
c:\windows\         # nothing, pathname ends with the dir 'windows'
c:hello.txt         # matches potential filename 'hello.txt'
~it_s_me/.bashrc    # matches potential filename '.bashrc'
c:\windows\system32 # matches potential filename 'system32', except
                    # that is obviously a dir. os.path.is_dir()
                    # should be used to tell us for sure

Il regex può essere testato qui .


stai usando re, perché non il modulo os?
Saurabh Chandra Patel,

@SaurabhChandraPatel è passato tanto tempo. Se ricordo bene, in questo caso regex viene utilizzato come soluzione multipiattaforma. Ad esempio, è possibile elaborare i nomi dei file di Windows su un server Linux.
Eric Duminil,

5

Forse solo la mia soluzione tutto in uno senza importanti novità (per quanto riguarda il tempfile per creare file temporanei: D)

import tempfile
abc = tempfile.NamedTemporaryFile(dir='/tmp/')
abc.name
abc.name.replace("/", " ").split()[-1] 

Ottenere i valori di abc.namesarà una stringa come questa: '/tmp/tmpks5oksk7' così posso sostituire il /con uno spazio .replace("/", " ")e quindi chiamare split(). Ciò restituirà un elenco e ottengo l'ultimo elemento dell'elenco con[-1]

Non è necessario importare alcun modulo.


2
Cosa succede se il nome file o una directory contiene uno spazio?
Kriss,

1
Che dire di una divisione diretta ("/") [- 1]?
Nan,

4

Non ho mai visto percorsi a doppia barra rovesciata, esistono? La funzione integrata del modulo Python osnon riesce per quelli. Tutti gli altri funzionano, anche l'avvertimento dato da te con os.path.normpath():

paths = ['a/b/c/', 'a/b/c', '\\a\\b\\c', '\\a\\b\\c\\', 'a\\b\\c', 
...     'a/b/../../a/b/c/', 'a/b/../../a/b/c', 'a/./b/c', 'a\b/c']
for path in paths:
    os.path.basename(os.path.normpath(path))

Quelli non sono doppi backslahes. Sono singole barre rovesciate e devono essere sfuggite.
Eric Duminil,

3

Il separatore di Windows può trovarsi in un nome file Unix o Percorso Windows. Il separatore Unix può esistere solo nel percorso Unix. La presenza di un separatore Unix indica un percorso non Windows.

Quanto segue rimuoverà (taglia il separatore finale) dal separatore specifico del sistema operativo, quindi dividerà e restituirà il valore più a destra. È brutto, ma semplice basato sul presupposto sopra. Se il presupposto non è corretto, si prega di aggiornare e aggiornerò questa risposta per soddisfare le condizioni più precise.

a.rstrip("\\\\" if a.count("/") == 0 else '/').split("\\\\" if a.count("/") == 0 else '/')[-1]

codice di esempio:

b = ['a/b/c/','a/b/c','\\a\\b\\c','\\a\\b\\c\\','a\\b\\c','a/b/../../a/b/c/','a/b/../../a/b/c']

for a in b:

    print (a, a.rstrip("\\" if a.count("/") == 0 else '/').split("\\" if a.count("/") == 0 else '/')[-1])

1
Inoltre, sentiti libero di inviarmi suggerimenti su come formattare in questa sede. Ha preso mezza dozzina di tentativi per ottenere il codice di esempio in atto.
dusc2don,

1

Per completezza, ecco la pathlibsoluzione per Python 3.2+:

>>> from pathlib import PureWindowsPath

>>> paths = ['a/b/c/', 'a/b/c', '\\a\\b\\c', '\\a\\b\\c\\', 'a\\b\\c', 
...          'a/b/../../a/b/c/', 'a/b/../../a/b/c']

>>> [PureWindowsPath(path).name for path in paths]
['c', 'c', 'c', 'c', 'c', 'c', 'c']

Funziona su Windows e Linux.


1

In Python 2 e 3, usando il modulo pathlib2 :

import posixpath  # to generate unix paths
from pathlib2 import PurePath, PureWindowsPath, PurePosixPath

def path2unix(path, nojoin=True, fromwinpath=False):
    """From a path given in any format, converts to posix path format
    fromwinpath=True forces the input path to be recognized as a Windows path (useful on Unix machines to unit test Windows paths)"""
    if not path:
        return path
    if fromwinpath:
        pathparts = list(PureWindowsPath(path).parts)
    else:
        pathparts = list(PurePath(path).parts)
    if nojoin:
        return pathparts
    else:
        return posixpath.join(*pathparts)

Uso:

In [9]: path2unix('lala/lolo/haha.dat')
Out[9]: ['lala', 'lolo', 'haha.dat']

In [10]: path2unix(r'C:\lala/lolo/haha.dat')
Out[10]: ['C:\\', 'lala', 'lolo', 'haha.dat']

In [11]: path2unix(r'C:\lala/lolo/haha.dat') # works even with malformatted cases mixing both Windows and Linux path separators
Out[11]: ['C:\\', 'lala', 'lolo', 'haha.dat']

Con la tua testcase:

In [12]: testcase = paths = ['a/b/c/', 'a/b/c', '\\a\\b\\c', '\\a\\b\\c\\', 'a\\b\\c',
    ...: ...     'a/b/../../a/b/c/', 'a/b/../../a/b/c']

In [14]: for t in testcase:
    ...:     print(path2unix(t)[-1])
    ...:
    ...:
c
c
c
c
c
c
c

L'idea qui è quella di convertire tutti i percorsi nella rappresentazione interna unificata di pathlib2, con diversi decodificatori a seconda della piattaforma. Fortunatamente, pathlib2include un decodificatore generico chiamato PurePathche dovrebbe funzionare su qualsiasi percorso. Nel caso in cui ciò non funzioni, è possibile forzare il riconoscimento del percorso di Windows utilizzando fromwinpath=True. Questo dividerà la stringa di input in parti, l'ultima è la foglia che stai cercando, quindi la path2unix(t)[-1].

Se l'argomento nojoin=False, il percorso verrà unito di nuovo, in modo che l'output sia semplicemente la stringa di input convertita in un formato Unix, che può essere utile per confrontare i percorsi secondari tra piattaforme.

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.