Extrahieren Sie den Dateinamen aus dem Pfad, unabhängig vom Betriebssystem- / Pfadformat

794

Mit welcher Python-Bibliothek kann ich Dateinamen aus Pfaden extrahieren, unabhängig vom Betriebssystem oder Pfadformat?

Zum Beispiel möchte ich, dass alle diese Pfade mich zurückgeben 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
Summen
quelle

Antworten:

781

Die Verwendung von os.path.splitoder os.path.basenamewie von anderen vorgeschlagen, funktioniert nicht in allen Fällen: Wenn Sie das Skript unter Linux ausführen und versuchen, einen klassischen Windows-Pfad zu verarbeiten, schlägt dies fehl.

Windows-Pfade können entweder einen Schrägstrich oder einen Schrägstrich als Pfadtrennzeichen verwenden. Daher ntpathfunktioniert das Modul (das unter Windows os.path entspricht) für alle (1) Pfade auf allen Plattformen.

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

Wenn die Datei mit einem Schrägstrich endet, ist der Basisname natürlich leer. Erstellen Sie also Ihre eigene Funktion, um damit umzugehen:

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

Nachprüfung:

>>> 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) Es gibt eine Einschränkung: Linux-Dateinamen können Backslashes enthalten . Unter Linux wird also r'a/b\c'immer auf die Datei b\cim aOrdner verwiesen, während unter Windows immer auf die cDatei im bUnterordner des aOrdners verwiesen wird. Also , wenn beide vorwärts und rückwärts Schrägstriche in einem Pfad verwendet werden, Sie müssen die zugehörige Plattform kennen, um es richtig zu interpretieren. In der Praxis ist es normalerweise sicher anzunehmen, dass es sich um einen Windows-Pfad handelt, da Backslashes in Linux-Dateinamen selten verwendet werden. Beachten Sie dies jedoch beim Codieren, damit keine versehentlichen Sicherheitslücken entstehen.

Lauritz V. Thaulow
quelle
29
os.pathLädt das ntpathModul unter Windows nur intern. Mit diesem Modul ist es möglich, die '\\'Pfadtrennzeichen auch auf Linux-Computern zu handhaben . Unter Linux vereinfacht das posixpathModul (bzw. os.path) die Pfadoperationen, um nur '/'Trennzeichen im Posix-Stil zuzulassen .
Moooeeeep
@moooeeeep Also könnten wir Stranacs Antwort verwenden, und sie ist zuverlässig? ( "Die Verwendung von os.path.split oder os.path.basename, wie andere vorschlagen, funktioniert nicht in allen Fällen: Wenn Sie das Skript unter Linux ausführen und versuchen, einen klassischen Pfad im Windows-Stil zu verarbeiten, schlägt dies fehl" - - das Zitat stammt aus Lauritz 'Post - und ich verstehe nicht, ob diese Warnung Stranacs Antwort betrifft oder nicht).
John CJ
3
@ johnc.j. Nur wenn Sie Windows-Pfade (z. B. r'C:\path\to\file.txt') auf einem Linux-Computer analysieren müssen , müssen Sie das ntpath-Modul verwenden. Andernfalls können Sie die Funktionen von os.path verwenden. Dies liegt daran, dass Linux-Systeme normalerweise die Verwendung der Backslash-Zeichen in Dateinamen zulassen (wie in der Antwort erläutert).
Moooeeeep
2
Entspricht Ihre Lösung nicht os.path.basename(os.path.normpath(path))?
Mr_and_Mrs_D
2
Für das, was es zukünftigen Besuchern dieser Frage wert ist, bin ich auf die Situation gestoßen, vor der Lauritz gewarnt hat, und seine Lösung war die einzige, die funktioniert hat. Kein Finangling mit os konnte nur den Dateinamen ausgeben. Also imho, ntpath ist der richtige Weg.
Harabeck
1250

Tatsächlich gibt es eine Funktion , die genau das zurückgibt, was Sie wollen

import os
print(os.path.basename(your_path))
stranac
quelle
22
Wenn Sie Pfade auf betriebssystemunabhängige Weise verarbeiten möchten, erwarten Sie für os.path.basename (u "C: \\ temp \\ bla.txt") 'bla.txt'. Bei der Frage geht es nicht darum, einen gültigen Dateinamen zu erhalten, sondern den Namen für einen Pfad zu extrahieren.
Adi Roiban
3
Bei meiner Google-Suche nach dem Dateinamen eines Pfads war diese Antwort am hilfreichsten. Mein Anwendungsfall ist sowieso nur unter Windows.
Bobort
2
os.path.basename(your_path)Das hat funktioniert! Ich wollte Skriptpfad: os.path.dirname(os.path.realpath(__file__))und Skriptname : os.path.basename(os.path.realpath(__file__)). Vielen Dank!
TheWalkingData
@AdiRoiban Könnten Sie bitte Ihren Kommentar ausarbeiten? Ich habe es unter Windows 7 getestet und bekomme tatsächlich "bla.txt". Einfach gesagt, ich sehe kein Problem (für mich selbst).
John CJ
10
@ johnc.j. Der Punkt ist, wenn Sie dies unter Linux versucht haben, würden Sie 'C:\\temp\\bla.txt'stattdessen bekommen .
Moooeeeep
218

os.path.split ist die Funktion, nach der Sie suchen

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

>>> print(tail)
a.dat
>>> print(head)
/tmp/d
Jakob Bowyer
quelle
40
Nur damit andere Benutzer vorsichtig sein können, wird "" zurückgegeben, wenn die Pfade mit "/" oder "\"
enden
Wenn ich "C: \ Benutzer \ Dell \ Desktop \ ProjectShadow \ button \ button.py" versuche, wird das "ProjectShadow utton tton" für alles andere zurückgegeben, und es wird das richtige Ergebnis zurückgegeben
amitnair92
4
@ amitnair92 - Führen Sie entweder Folgendes aus: r "C: \ Benutzer \ Dell \ Desktop \ ProjectShadow \ button \ button.py" oder Folgendes: "C: \\ Benutzer \\ Dell \\ Desktop \\ ProjectShadow \\ Schaltfläche \\ Schaltfläche .py "-" \ b "ist ein Sonderzeichen (System 'Glocke', glaube ich), ähnlich wie \ r oder \ n Zeilenumbruch / Wagenrücklauf bedeuten. Wenn Sie dem String r "C: \ ..." voranstellen, verwenden Sie die angegebene Roheingabe
Bruce Lamond,
87

In Python 3

>>> from pathlib import Path    
>>> Path("/tmp/d/a.dat").name
'a.dat'
Kishan B.
quelle
3.4 bis 3.6 oder höher, je nachdem, welche Pathlib-Elemente Sie verwenden.
LightCC
8
kann auch Path ("some / path / to / file.dat"). stem verwenden, um den Dateinamen ohne die Dateierweiterung zu erhalten
s2t2
18
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
Saurabh Chandra Patel
quelle
12

In Ihrem Beispiel müssen Sie auch den Schrägstrich von rechts auf der rechten Seite entfernen, um zurückzukehren c:

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

Zweites Level:

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

Update: Ich denke, lazyrhat die richtige Antwort geliefert. Mein Code funktioniert nicht mit Windows-ähnlichen Pfaden auf Unix-Systemen und umgekehrt mit Unix-ähnlichen Pfaden auf Windows-Systemen.

Ski
quelle
Ihre Antwort funktioniert weder r"a\b\c"unter Linux noch "a/b/c"unter Windows.
Lauritz V. Thaulow
os.path.basename(path)funktioniert natürlich nur wenn es os.path.isfile(path)ist True. Daher path = 'a/b/c/'ist überhaupt kein gültiger Dateiname ...
moooeeeep
1
@fmaas os.path.basename ist eine reine String-Verarbeitungsfunktion. Es ist egal, ob die Datei existiert oder ob es sich um eine Datei oder ein Verzeichnis handelt. os.path.basename("a/b/c/")kehrt ""wegen des abschließenden Schrägstrichs zurück.
Lauritz V. Thaulow
lazyrSie haben Recht! Daran habe ich nicht gedacht. Wäre es sicher, es einfach zu tun path = path.replace('\\', '/')?
Ski
@Skirmantas Ich nehme an, aber es fühlt sich nicht richtig an. Ich denke, die Pfadverarbeitung sollte mit den integrierten Tools erfolgen, die für den Job erstellt wurden. Es gibt viel mehr zu Wege als das Auge.
Lauritz V. Thaulow
11
fname = str("C:\Windows\paint.exe").split('\\')[-1:][0]

Dies wird zurückgeben: paint.exe

Ändern Sie den sep-Wert der Split-Funktion in Bezug auf Ihren Pfad oder Ihr Betriebssystem.

Eslam Hamouda
quelle
Dies ist die Antwort, die mir gefallen hat, aber warum nicht einfach Folgendes tun? fname = str(path).split('/')[-1]
asultan904
10

Wenn Sie den Dateinamen automatisch erhalten möchten, können Sie dies tun

import glob

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

Wenn Ihr Dateipfad nicht mit "/" und durch "/" getrennten Verzeichnissen endet, verwenden Sie den folgenden Code. Wie wir allgemein wissen, endet der Pfad nicht mit "/".

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

In einigen Fällen, in denen URLs mit "/" enden, wird der folgende Code verwendet

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

Wenn Ihr Pfad jedoch durch "\" getrennt ist, was Sie normalerweise in Windows-Pfaden finden, können Sie die folgenden Codes verwenden

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

Sie können beide zu einer Funktion kombinieren, indem Sie den Betriebssystemtyp überprüfen und das Ergebnis zurückgeben.

Santosh Kumar Manda
quelle
7

Dies funktioniert auch für Linux und Windows mit Standardbibliothek

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]

Ergebnisse:

['c', 'c', 'c', 'c', 'c', 'c', 'c']
Csabka
quelle
6

Hier ist eine reine Regex-Lösung, die mit jedem Betriebssystempfad auf jedem Betriebssystem zu funktionieren scheint.

Es wird kein anderes Modul benötigt und es ist auch keine Vorverarbeitung erforderlich:

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']

Aktualisieren:

Wenn Sie nur einen potenziellen Dateinamen wünschen , falls vorhanden (dh ein Verzeichnis /a/b/und so ist c:\windows\), ändern Sie den regulären Ausdruck in : r'[^\\/]+(?![\\/])$'. Für den "herausgeforderten regulären Ausdruck" ändert dies den positiven Forward-Lookahead für eine Art Schrägstrich in einen negativen Forward-Lookahead, wodurch Pfadnamen, die mit diesem Schrägstrich enden, nichts anstelle des letzten Unterverzeichnisses im Pfadnamen zurückgeben. Natürlich gibt es keine Garantie dafür, dass sich der potenzielle Dateiname tatsächlich auf eine Datei bezieht und dafür verwendet werden müsste os.path.is_dir()oder os.path.is_file()müsste.

Dies wird wie folgt übereinstimmen:

/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

Der Regex kann hier getestet werden .

Eric Duminil
quelle
Sie verwenden re, warum nicht OS-Modul?
Saurabh Chandra Patel
@ SaurabhChandraPatel es ist lange her. Wenn ich mich richtig erinnere, wird in diesem Fall Regex als plattformübergreifende Lösung verwendet. Sie können Windows-Dateinamen beispielsweise auf einem Linux-Server verarbeiten.
Eric Duminil
5

Vielleicht nur meine All-in-One-Lösung ohne wichtige neue (siehe Tempfile zum Erstellen temporärer Dateien: D)

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

Das Erhalten der Werte von abc.namewird eine Zeichenfolge wie die folgende sein: '/tmp/tmpks5oksk7' Also kann ich die /durch ein Leerzeichen ersetzen .replace("/", " ")und dann aufrufen split(). Das gibt eine Liste zurück und ich bekomme das letzte Element der Liste mit[-1]

Es muss kein Modul importiert werden.

Akendo
quelle
2
Was ist, wenn der Dateiname oder ein Verzeichnis ein Leerzeichen enthält?
kriss
1
Was ist mit einem direkten Split ("/") [- 1]?
Nan
4

Ich habe noch nie Pfade mit doppeltem Schrägstrich gesehen. Gibt es sie? Die integrierte Funktion des Python-Moduls osschlägt für diese fehl. Alle anderen arbeiten, auch die von Ihnen gegebene Einschränkung mit 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))
PythoNic
quelle
Das sind keine doppelten Backslahes. Sie sind einzelne Backslashes und müssen entkommen.
Eric Duminil
3

Das Windows-Trennzeichen kann sich in einem Unix-Dateinamen oder einem Windows-Pfad befinden. Das Unix-Trennzeichen kann nur im Unix-Pfad vorhanden sein. Das Vorhandensein eines Unix-Trennzeichens weist auf einen Nicht-Windows-Pfad hin.

Im Folgenden wird das hintere Trennzeichen durch das betriebssystemspezifische Trennzeichen entfernt (abgeschnitten), dann geteilt und der Wert ganz rechts zurückgegeben. Es ist hässlich, aber einfach, basierend auf der obigen Annahme. Wenn die Annahme falsch ist, aktualisieren Sie bitte und ich werde diese Antwort aktualisieren, um den genaueren Bedingungen zu entsprechen.

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

Beispielcode:

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])
dusc2don
quelle
1
Sie können mir auch gerne Hinweise zum Formatieren an diesem Ort senden. Es dauerte ein halbes Dutzend Versuche, den Beispielcode zu installieren.
dusc2don
1

Der Vollständigkeit halber ist hier die pathlibLösung für 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']

Dies funktioniert sowohl unter Windows als auch unter Linux.

Morgoth
quelle
1

Verwenden Sie in Python 2 und 3 das Modul 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)

Verwendungszweck:

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']

Mit Ihrem Testfall:

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

Hier geht es darum, alle Pfade in die einheitliche interne Darstellung von zu konvertieren pathlib2, wobei je nach Plattform unterschiedliche Decoder vorhanden sind. Glücklicherweise pathlib2enthält ein generischer Decoder namens PurePath, der auf jedem Pfad funktionieren sollte. Falls dies nicht funktioniert, können Sie die Erkennung des Windows-Pfads mithilfe von erzwingen fromwinpath=True. Dadurch wird die Eingabezeichenfolge in Teile aufgeteilt. Das letzte ist das gesuchte Blatt, daher daspath2unix(t)[-1] .

Wenn das Argument verwendet wird nojoin=False, wird der Pfad wieder verknüpft, sodass die Ausgabe einfach die in ein Unix-Format konvertierte Eingabezeichenfolge ist. Dies kann hilfreich sein, um Unterpfade plattformübergreifend zu vergleichen.

gaborous
quelle