Ekstrak nama file dari path, apa pun format os / pathnya

794

Pustaka Python mana yang bisa saya gunakan untuk mengekstrak nama file dari path, tidak peduli apa sistem operasi atau format path bisa?

Misalnya, saya ingin semua jalur ini mengembalikan saya 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
Berdengung
sumber

Jawaban:

781

Menggunakan os.path.splitatauos.path.basename seperti yang orang lain sarankan tidak akan berfungsi dalam semua kasus: jika Anda menjalankan skrip di Linux dan mencoba untuk memproses jalur gaya windows klasik, itu akan gagal.

Jalur Windows dapat menggunakan backslash atau forward slash sebagai pemisah jalur. Oleh karena itu, ntpathmodul (yang setara dengan os.path saat berjalan di windows) akan bekerja untuk semua (1) jalur di semua platform.

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

Tentu saja, jika file berakhir dengan garis miring, nama dasarnya akan kosong, jadi buatlah fungsi Anda sendiri untuk menghadapinya:

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

Verifikasi:

>>> 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) Ada satu peringatan: nama file Linux mungkin mengandung garis miring terbalik . Jadi di linux, r'a/b\c'selalu merujuk ke file b\cdi afolder, sedangkan di Windows, selalu merujuk ke cfile di bsubfolder afolder. Jadi, ketika garis miring maju dan mundur digunakan di jalur, Anda perlu mengetahui platform terkait untuk dapat menafsirkannya dengan benar. Dalam praktiknya, biasanya aman untuk menganggapnya sebagai jalur windows karena backslash jarang digunakan dalam nama file Linux, tetapi ingatlah ini ketika Anda membuat kode sehingga Anda tidak membuat lubang keamanan yang tidak disengaja.

Lauritz V. Thaulow
sumber
29
pada Windows, os.pathmuat ntpathmodul secara internal. Dengan menggunakan modul ini, dimungkinkan untuk menangani '\\'separator path bahkan pada mesin Linux. Untuk Linux posixpathmodul (resp. os.path) Akan menyederhanakan operasi jalur untuk memungkinkan hanya '/'pemisah gaya posix.
moooeeeep
@ooooeeeee Jadi kita bisa menggunakan jawaban Stranac, dan ini bisa diandalkan? ( "Menggunakan os.path.split atau os.path.basename seperti yang disarankan orang lain tidak akan berfungsi dalam semua kasus: jika Anda menjalankan skrip di Linux dan berusaha memproses jalur gaya windows klasik, itu akan gagal" - - Kutipan dari posting Lauritz - dan saya tidak mengerti, apakah peringatan ini menyangkut jawaban Stranac, atau tidak).
john cj
3
@ johnc.j. Hanya ketika Anda perlu mengurai jalur gaya Windows (misalnya, r'C:\path\to\file.txt') pada mesin Linux, Anda perlu menggunakan modul ntpath. Jika tidak, Anda dapat menggunakan fungsi dari os.path. Ini karena sistem Linux biasanya memungkinkan penggunaan karakter backslash dalam nama file (seperti yang dijelaskan dalam jawaban).
moooeeeep
2
Bukankah solusi Anda setara dengan os.path.basename(os.path.normpath(path))?
Mr_and_Mrs_D
2
Untuk apa yang layak bagi pengunjung masa depan untuk pertanyaan ini, saya mengalami situasi yang diperingatkan Lauritz dan solusinya adalah satu-satunya yang berhasil. Tidak ada masalah dengan os yang bisa menampilkan hanya nama file. Jadi imho, ntpath adalah jalan yang harus ditempuh.
Harabeck
1250

Sebenarnya, ada fungsi yang mengembalikan apa yang Anda inginkan

import os
print(os.path.basename(your_path))
stranac
sumber
22
Jika Anda ingin memproses jalur dalam cara yang independen OS, maka untuk os.path.basename (u "C: \\ temp \\ bla.txt") Anda mengharapkan untuk mendapatkan 'bla.txt'. Pertanyaannya bukan tentang mendapatkan nama file yang valid, tetapi mengekstraksi nama untuk path.
Adi Roiban
3
Di pencarian Google saya untuk menemukan nama file jalan, jawaban ini adalah yang paling bermanfaat. Kasus penggunaan saya hanya pada Windows saja.
Bobort
2
os.path.basename(your_path)Ini berhasil! Saya ingin skrip jalan: os.path.dirname(os.path.realpath(__file__))dan nama skrip: os.path.basename(os.path.realpath(__file__)). Terima kasih!
TheWalkingData
@AdiRoiban Bisakah Anda jelaskan komentar Anda? Saya mengujinya pada Windows 7 dan saya benar-benar mendapatkan "bla.txt '. Cukup dengan mengatakan, saya tidak melihat masalah (untuk diri saya sendiri).
john cj
10
@ johnc.j. Intinya adalah, ketika Anda mencoba ini di Linux, Anda akan mendapatkannya 'C:\\temp\\bla.txt'.
moooeeeep
218

os.path.split adalah fungsi yang Anda cari

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

>>> print(tail)
a.dat
>>> print(head)
/tmp/d
Jakob Bowyer
sumber
40
Hanya untuk pengguna lain yang berhati-hati, ini mengembalikan "" jika jalur berakhir pada "/" atau "\"
BuZz
Ketika saya mencoba "C: \ Users \ Dell \ Desktop \ ProjectShadow \ button \ button.py" ia mengembalikan th "ProjectShadow utton tton" untuk semua selain ini mengembalikan hasil yang benar
amitnair92
4
@ amitnair92 - Lakukan ini: r "C: \ Users \ Dell \ Desktop \ ProjectShadow \ button \ button.py" atau ini: "C: \\ Users \\ Dell \\ Desktop \\ ProjectShadow \\ tombol \\ tombol \\ .py "-" \ b "adalah karakter khusus (sistem 'bell' menurut saya), mirip dengan bagaimana \ r atau \ n menandakan baris baru / carriage return. Mengawali ulang string dengan r "C: \ ..." berarti menggunakan input mentah yang diberikan
Bruce Lamond
87

Dengan python 3

>>> from pathlib import Path    
>>> Path("/tmp/d/a.dat").name
'a.dat'
Kishan B
sumber
3,4 hingga 3,6 atau lebih baru, tergantung item pathlib mana yang Anda gunakan.
LightCC
8
juga dapat menggunakan Path ("some / path / ke / file.dat"). Batang untuk mendapatkan nama file tanpa ekstensi file
s2t2
47
import os
head, tail = os.path.split('path/to/file.exe')

ekor adalah apa yang Anda inginkan, nama file.

Lihat dokumen modul python os untuk detailnya

nomor 5
sumber
13
Hanya untuk pengguna lain yang berhati-hati, ini mengembalikan "" jika jalur berakhir pada "/" atau "\"
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
sumber
12

Dalam contoh Anda, Anda juga perlu menghapus garis miring dari kanan untuk kembali c:

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

Tingkat kedua:

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

pembaruan: Saya pikir lazyrtelah memberikan jawaban yang benar. Kode saya tidak akan bekerja dengan jalur seperti Windows di sistem unix dan sebaliknya dibandingkan dengan jalur seperti di sistem windows.

Main ski
sumber
Jawaban Anda tidak akan berfungsi untuk r"a\b\c"di linux, atau untuk "a/b/c"di windows.
Lauritz V. Thaulow
tentu saja, os.path.basename(path)hanya akan berfungsi jika os.path.isfile(path)ada True. Karena itu path = 'a/b/c/'sama sekali bukan nama file yang valid ...
moooeeeep
1
@fmaas os.path.basename adalah murni fungsi pemrosesan string. Tidak peduli apakah file itu ada atau apakah itu file atau dir. os.path.basename("a/b/c/")kembali ""karena garis miring.
Lauritz V. Thaulow
lazyrkamu benar! Saya tidak memikirkan hal itu. Apakah aman untuk melakukannya path = path.replace('\\', '/')?
Ski
@Skirmantas kurasa, tapi rasanya tidak benar. Saya pikir pemrosesan jalur harus dilakukan dengan alat bawaan yang dibuat untuk pekerjaan itu. Ada banyak hal yang harus dilalui daripada bertemu dengan mata.
Lauritz V. Thaulow
11
fname = str("C:\Windows\paint.exe").split('\\')[-1:][0]

ini akan kembali: paint.exe

ubah nilai sep fungsi split terkait jalur atau OS Anda.

Eslam Hamouda
sumber
Ini adalah jawaban yang saya sukai, tetapi mengapa tidak melakukan yang berikut? fname = str(path).split('/')[-1]
asultan904
10

Jika Anda ingin mendapatkan nama file secara otomatis, Anda dapat melakukannya

import glob

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

Jika jalur file Anda tidak berakhir dengan "/" dan direktori dipisahkan oleh "/" maka gunakan kode berikut. Seperti yang kita ketahui umumnya path tidak berakhir dengan "/".

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

Tetapi dalam beberapa kasus seperti URL diakhiri dengan "/" lalu gunakan kode berikut

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

tetapi ketika path Anda di-sperated oleh "\" yang biasanya Anda temukan di path windows maka Anda dapat menggunakan kode-kode berikut

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

Anda dapat menggabungkan keduanya menjadi satu fungsi dengan memeriksa tipe OS dan mengembalikan hasilnya.

Santosh kumar Manda
sumber
7

Ini berfungsi untuk linux dan windows juga dengan perpustakaan standar

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]

Hasil:

['c', 'c', 'c', 'c', 'c', 'c', 'c']
Csabka
sumber
6

Inilah solusi khusus regex, yang tampaknya berfungsi dengan jalur OS apa pun pada OS apa pun.

Tidak diperlukan modul lain, dan tidak perlu preprocessing:

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

Memperbarui:

Jika Anda hanya ingin potensial nama file, jika ada (yaitu, /a/b/adalah dir dan begitu juga c:\windows\), mengubah regex untuk: r'[^\\/]+(?![\\/])$'. Untuk "regex challengeed," ini mengubah lookahead maju positif untuk semacam garis miring menjadi lookahead maju negatif, menyebabkan nama path yang berakhir dengan slash tersebut tidak menghasilkan apa-apa alih-alih sub-direktori terakhir dalam pathname. Tentu saja tidak ada jaminan bahwa nama file potensial sebenarnya mengacu pada file dan untuk itu os.path.is_dir()atau os.path.is_file()perlu dipekerjakan.

Ini akan cocok sebagai berikut:

/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

Regex dapat diuji di sini .

Eric Duminil
sumber
Anda menggunakan kembali, mengapa tidak modul os?
Saurabh Chandra Patel
@SaurabhChandraPatel sudah lama. Jika saya ingat dengan benar, regex digunakan sebagai solusi lintas platform dalam kasus ini. Anda dapat memproses nama file windows di server Linux, misalnya.
Eric Duminil
5

Mungkin hanya solusi all in one saya tanpa beberapa yang baru (anggap tempfile untuk membuat file sementara: D)

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

Mendapatkan nilai abc.nameakan menjadi string seperti ini: '/tmp/tmpks5oksk7' Jadi saya bisa mengganti /dengan spasi .replace("/", " ")dan kemudian memanggil split(). Itu akan mengembalikan daftar dan saya mendapatkan elemen terakhir dari daftar[-1]

Tidak perlu mengimpor modul apa pun.

Akendo
sumber
2
Bagaimana jika nama file atau direktori berisi spasi?
Kriss
1
Bagaimana dengan pemisahan langsung ("/") [- 1]?
Nan
4

Saya belum pernah melihat jalur backslashed ganda, apakah mereka ada? Fitur bawaan modul python osgagal untuk mereka. Semua yang lain berfungsi, juga peringatan yang diberikan oleh Anda dengan 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
sumber
Itu bukan backslahes ganda. Mereka adalah backslash tunggal, dan mereka harus melarikan diri.
Eric Duminil
3

Pemisah Windows dapat berupa nama file Unix atau Windows Path. Pemisah Unix hanya bisa ada di jalur Unix. Kehadiran pemisah Unix menunjukkan jalur non-Windows.

Berikut ini akan menghapus (memotong trailing separator) oleh pemisah khusus OS, kemudian membelah dan mengembalikan nilai paling kanan. Itu jelek, tapi sederhana berdasarkan asumsi di atas. Jika asumsi ini salah, harap perbarui dan saya akan memperbarui respons ini agar sesuai dengan ketentuan yang lebih akurat.

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

Kode sampel:

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
sumber
1
Juga, jangan ragu untuk mengirim saya petunjuk tentang cara memformat di tempat ini. Butuh setengah lusin mencoba untuk mendapatkan kode sampel di tempat.
dusc2don
1

Demi kelengkapan, berikut adalah pathlibsolusi untuk 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']

Ini berfungsi pada Windows dan Linux.

Morgoth
sumber
1

Dalam Python 2 dan 3, menggunakan 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)

Pemakaian:

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

Dengan testcase Anda:

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

Idenya di sini adalah untuk mengubah semua jalur menjadi representasi internal terpadu pathlib2, dengan decoder yang berbeda tergantung pada platform. Untungnya, pathlib2termasuk decoder generik yang disebut PurePathyang harus bekerja pada jalur apa pun. Jika ini tidak berhasil, Anda dapat memaksa pengenalan jalur windows menggunakan fromwinpath=True. Ini akan membagi string input menjadi beberapa bagian, yang terakhir adalah daun yang Anda cari, karenanya path2unix(t)[-1].

Jika argumen nojoin=False, jalur akan bergabung kembali, sehingga output hanyalah string input yang dikonversi ke format Unix, yang dapat bermanfaat untuk membandingkan subpath di seluruh platform.

gaborous
sumber