Extraire le nom du fichier du chemin, quel que soit le format os / path

794

Quelle bibliothèque Python puis-je utiliser pour extraire les noms de fichiers des chemins, quels que soient le système d'exploitation ou le format de chemin?

Par exemple, j'aimerais que tous ces chemins me renvoient 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
Bourdonner
la source

Réponses:

781

Utiliser os.path.splitou os.path.basenamecomme d'autres le suggèrent ne fonctionnera pas dans tous les cas: si vous exécutez le script sous Linux et essayez de traiter un chemin de style Windows classique, il échouera.

Les chemins Windows peuvent utiliser une barre oblique inverse ou une barre oblique comme séparateur de chemin. Par conséquent, le ntpathmodule (qui est équivalent à os.path lors de l'exécution sur Windows) fonctionnera pour tous les (1) chemins sur toutes les plates-formes.

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

Bien sûr, si le fichier se termine par une barre oblique, le nom de base sera vide, alors créez votre propre fonction pour y faire face:

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

Vérification:

>>> 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) Il y a une mise en garde: les noms de fichiers Linux peuvent contenir des barres obliques inverses . Donc, sous Linux, r'a/b\c'fait toujours référence au fichier b\cdans le adossier, tandis que sous Windows, il fait toujours référence au cfichier dans le bsous- adossier du dossier. Ainsi, lorsque des barres obliques avant et arrière sont utilisées dans un chemin, vous devez connaître la plate-forme associée pour pouvoir l'interpréter correctement. Dans la pratique, il est généralement prudent de supposer qu'il s'agit d'un chemin Windows, car les barres obliques inverses sont rarement utilisées dans les noms de fichiers Linux, mais gardez cela à l'esprit lorsque vous codez afin de ne pas créer de failles de sécurité accidentelles.

Lauritz V. Thaulow
la source
29
sous Windows, os.pathcharge simplement le ntpathmodule en interne. En utilisant ce module, il est possible de gérer les '\\'séparateurs de chemin même sur les machines Linux. Pour Linux, le posixpathmodule (resp. os.path) Simplifiera les opérations de chemin pour n'autoriser que les '/'séparateurs de style posix .
moooeeeep
@moooeeeep Nous pourrions donc utiliser la réponse de Stranac, et elle est fiable? ( "Utiliser os.path.split ou os.path.basename comme d'autres le suggèrent ne fonctionnera pas dans tous les cas: si vous exécutez le script sous Linux et essayez de traiter un chemin classique de style Windows, il échouera" - - la citation est du post de Lauritz - et je ne comprends pas, cet avertissement concerne-t-il la réponse de Stranac, ou non).
john cj
3
@ johnc.j. Ce n'est que lorsque vous devez analyser des chemins de style Windows (par exemple, r'C:\path\to\file.txt') sur une machine Linux, que vous devez utiliser le module ntpath. Sinon, vous pouvez utiliser les fonctions de os.path. Cela est dû au fait que les systèmes Linux autorisent normalement l'utilisation de la barre oblique inverse dans les noms de fichiers (comme expliqué dans la réponse).
moooeeeep
2
Votre solution n'est-elle pas équivalente à os.path.basename(os.path.normpath(path))?
Mr_and_Mrs_D
2
Pour ce que cela vaut pour les futurs visiteurs de cette question, je suis tombé sur la situation sur laquelle Lauritz mettait en garde et sa solution était la seule qui fonctionnait. Aucun assemblage avec os ne pourrait produire uniquement le nom de fichier. Alors à mon humble avis, ntpath est la voie à suivre.
Harabeck
1250

En fait, il y a une fonction qui retourne exactement ce que vous voulez

import os
print(os.path.basename(your_path))
stranac
la source
22
Si vous souhaitez traiter les chemins de manière indépendante du système d'exploitation, alors pour os.path.basename (u "C: \\ temp \\ bla.txt"), vous vous attendez à obtenir 'bla.txt'. La question n'est pas d'obtenir un nom de fichier valide, mais d'extraire le nom d'un chemin.
Adi Roiban
3
Lors de ma recherche Google pour trouver le nom de fichier d'un chemin, cette réponse a été la plus utile. Mon cas d'utilisation est uniquement sur Windows de toute façon.
Bobort
2
os.path.basename(your_path)Ça a marché! Je voulais chemin du script: os.path.dirname(os.path.realpath(__file__))et nom du script: os.path.basename(os.path.realpath(__file__)). Merci!
TheWalkingData
@AdiRoiban Pourriez-vous s'il vous plaît développer votre commentaire? Je l'ai testé sur Windows 7 et j'obtiens en fait "bla.txt '. Simplement en disant, je ne vois aucun problème (pour moi).
john cj
10
@ johnc.j. Le fait est que lorsque vous avez tenté cela sous Linux, vous obtiendrez à la 'C:\\temp\\bla.txt'place.
moooeeeep
218

os.path.split est la fonction que vous recherchez

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

>>> print(tail)
a.dat
>>> print(head)
/tmp/d
Jakob Bowyer
la source
40
Juste pour que les autres utilisateurs fassent attention, cela renvoie "" si les chemins se terminent par "/" ou "\"
BuZz
Lorsque j'essaie "C: \ Users \ Dell \ Desktop \ ProjectShadow \ button \ button.py", il renvoie ce "ProjectShadow utton tton" pour tout autre chose que cela renvoie un résultat correct
amitnair92
4
@ amitnair92 - Faites ceci: r "C: \ Users \ Dell \ Desktop \ ProjectShadow \ button \ button.py" ou ceci: "C: \\ Users \\ Dell \\ Desktop \\ ProjectShadow \\ button \\ button .py "-" \ b "est un caractère spécial (système 'bell' je pense), similaire à la façon dont \ r ou \ n signifient retour à la ligne / chariot. Préfixer la chaîne avec r "C: \ ..." signifie utiliser l'entrée brute donnée
Bruce Lamond
87

En python 3

>>> from pathlib import Path    
>>> Path("/tmp/d/a.dat").name
'a.dat'
Kishan B
la source
3.4 à 3.6 ou version ultérieure, selon exactement les éléments pathlib que vous utilisez.
LightCC
8
peut également utiliser Path ("some / path / to / file.dat"). stem pour obtenir le nom de fichier sans l'extension de fichier
s2t2
47
import os
head, tail = os.path.split('path/to/file.exe')

queue est ce que vous voulez, le nom de fichier.

Voir la documentation du module os python pour plus de détails

numéro 5
la source
13
Juste pour que les autres utilisateurs fassent attention, cela renvoie "" si les chemins se terminent par "/" ou "\"
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
Saurabh Chandra Patel
la source
12

Dans votre exemple, vous devrez également supprimer la barre oblique de droite à droite pour revenir c:

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

Deuxième niveau:

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

mise à jour: je pense lazyra fourni la bonne réponse. Mon code ne fonctionnera pas avec des chemins de type Windows sur les systèmes Unix et vice-versa avec des chemins de type Unix sur le système Windows.

Ski
la source
Votre réponse ne fonctionnera pas pour r"a\b\c"sous Linux, ni pour "a/b/c"Windows.
Lauritz V. Thaulow
bien sûr, os.path.basename(path)ne fonctionnera que si os.path.isfile(path)c'est le cas True. Ce path = 'a/b/c/'n'est donc pas du tout un nom de fichier valide ...
moooeeeep
1
@fmaas os.path.basename est purement une fonction de traitement de chaîne. Peu importe si le fichier existe ou s'il s'agit d'un fichier ou d'un répertoire. os.path.basename("a/b/c/")renvoie en ""raison de la barre oblique de fin.
Lauritz V. Thaulow
lazyrtu as raison! Je n'y ai pas pensé. Serait-il sûr de le faire path = path.replace('\\', '/')?
Ski
@ Skirmantas je suppose, mais ça ne me semble pas correct. Je pense que le traitement du chemin devrait être fait avec les outils intégrés qui ont été conçus pour le travail. Il y a beaucoup plus de chemins que ce que l'on voit.
Lauritz V. Thaulow
11
fname = str("C:\Windows\paint.exe").split('\\')[-1:][0]

cela renverra: paint.exe

changez la valeur de sep de la fonction de division concernant votre chemin ou votre système d'exploitation.

Eslam Hamouda
la source
C'est la réponse que j'ai aimé, mais pourquoi ne pas simplement faire ce qui suit? fname = str(path).split('/')[-1]
asultan904
10

Si vous souhaitez obtenir le nom de fichier automatiquement, vous pouvez le faire

import glob

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

Si votre chemin de fichier ne se termine pas par "/" et les répertoires séparés par "/", utilisez le code suivant. Comme nous le savons, le chemin ne se termine généralement pas par "/".

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

Mais dans certains cas, comme les URL se terminent par "/", utilisez le code suivant

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

mais lorsque votre chemin est ponctué de "\" que vous trouvez généralement dans les chemins Windows, vous pouvez utiliser les codes suivants

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

Vous pouvez combiner les deux en une seule fonction en vérifiant le type de système d'exploitation et en renvoyant le résultat.

Santosh kumar Manda
la source
7

Cela fonctionne pour Linux et Windows ainsi qu'avec la bibliothèque 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]

Résultats:

['c', 'c', 'c', 'c', 'c', 'c', 'c']
Csabka
la source
6

Voici une solution uniquement regex, qui semble fonctionner avec n'importe quel chemin de système d'exploitation sur n'importe quel système d'exploitation.

Aucun autre module n'est nécessaire, et aucun prétraitement n'est nécessaire non plus:

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

Mise à jour:

Si vous voulez seulement un potentiel nom de fichier, le cas échéant (c. -à- /a/b/est un répertoire et est donc c:\windows\), changer l'expression rationnelle à: r'[^\\/]+(?![\\/])$'. Pour le «regex contesté», cela change le lookahead avant positif pour une sorte de barre oblique en un lookahead avant négatif, ce qui fait que les chemins d'accès qui se terminent par cette barre oblique ne retournent rien au lieu du dernier sous-répertoire du chemin d'accès. Bien sûr, il n'y a aucune garantie que le nom de fichier potentiel se réfère réellement à un fichier et pour cela os.path.is_dir()ou os.path.is_file()devrait être utilisé.

Cela correspondra comme suit:

/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

Le regex peut être testé ici .

Eric Duminil
la source
vous utilisez re, pourquoi pas le module os?
Saurabh Chandra Patel
@SaurabhChandraPatel ça fait longtemps. Si je me souviens bien, regex est utilisé comme solution multiplateforme dans ce cas. Vous pouvez par exemple traiter des noms de fichiers Windows sur un serveur Linux.
Eric Duminil
5

Peut-être juste ma solution tout-en-un sans importante nouvelle (considérez le fichier temporaire pour créer des fichiers temporaires: D)

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

Obtenir les valeurs de abc.namesera une chaîne comme celle-ci: '/tmp/tmpks5oksk7' je peux donc remplacer le /par un espace .replace("/", " "), puis appelersplit() . Cela renverra une liste et j'obtiens le dernier élément de la liste avec[-1]

Pas besoin d'importer un module.

Akendo
la source
2
Que faire si le nom de fichier ou un répertoire contient un espace?
kriss
1
Qu'en est-il d'une division directe ("/") [- 1]?
Nan
4

Je n'ai jamais vu de chemins à double contre-oblique, existent-ils? La fonctionnalité intégrée du module python oséchoue pour ceux-ci. Tous les autres fonctionnent, ainsi que la mise en garde que vous avez faite avec 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
la source
Ce ne sont pas des doubles backslahes. Ce sont des barres obliques inverses simples et elles doivent être échappées.
Eric Duminil
3

Le séparateur Windows peut être dans un nom de fichier Unix ou un chemin Windows. Le séparateur Unix ne peut exister que dans le chemin Unix. La présence d'un séparateur Unix indique un chemin non Windows.

Les éléments suivants seront supprimés (couper le séparateur de fin) par le séparateur spécifique au système d'exploitation, puis diviser et retourner la valeur la plus à droite. C'est moche, mais simple basé sur l'hypothèse ci-dessus. Si l'hypothèse est incorrecte, veuillez la mettre à jour et je mettrai à jour cette réponse pour qu'elle corresponde aux conditions les plus précises.

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

exemple de code:

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
la source
1
N'hésitez pas à m'envoyer des conseils sur la façon de formater dans ce lieu. A pris une demi-douzaine d'essais pour mettre l'exemple de code en place.
dusc2don
1

Par souci d'exhaustivité, voici la pathlibsolution pour 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']

Cela fonctionne sur Windows et Linux.

Morgoth
la source
1

En Python 2 et 3, en utilisant le module 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)

Usage:

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

Avec votre 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'idée ici est de convertir tous les chemins en une représentation interne unifiée de pathlib2, avec différents décodeurs selon la plateforme. Heureusement, pathlib2inclut un décodeur générique appelé PurePathqui devrait fonctionner sur n'importe quel chemin. Si cela ne fonctionne pas, vous pouvez forcer la reconnaissance du chemin d'accès Windows à l'aide de fromwinpath=True. Cela divisera la chaîne d'entrée en parties, la dernière est la feuille que vous recherchez, d'où le path2unix(t)[-1].

Si l'argument nojoin=False, le chemin sera joint en arrière, de sorte que la sortie est simplement la chaîne d'entrée convertie au format Unix, ce qui peut être utile pour comparer les sous-chemins entre les plates-formes.

généreux
la source