Trích xuất tên tệp từ đường dẫn, bất kể định dạng os / path là gì


794

Tôi có thể sử dụng thư viện Python nào để trích xuất tên tệp từ các đường dẫn, bất kể hệ điều hành hoặc định dạng đường dẫn có thể là gì?

Ví dụ: tôi muốn tất cả các đường dẫn này trả lại cho tôi 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

Câu trả lời:


781

Sử dụng os.path.splithoặc os.path.basenamenhư những người khác đề xuất sẽ không hoạt động trong mọi trường hợp: nếu bạn đang chạy tập lệnh trên Linux và cố gắng xử lý một đường dẫn kiểu cửa sổ cổ điển, nó sẽ thất bại.

Đường dẫn Windows có thể sử dụng dấu gạch chéo ngược hoặc dấu gạch chéo chuyển tiếp làm dấu phân cách đường dẫn. Do đó, ntpathmô-đun (tương đương với os.path khi chạy trên windows) sẽ hoạt động cho tất cả (1) đường dẫn trên tất cả các nền tảng.

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

Tất nhiên, nếu tệp kết thúc bằng dấu gạch chéo, tên cơ sở sẽ trống, vì vậy hãy tạo chức năng của riêng bạn để xử lý:

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

Xác minh:

>>> 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ó một cảnh báo: Tên tệp Linux có thể chứa dấu gạch chéo ngược . Vì vậy, trên linux, r'a/b\c'luôn đề cập đến tệp b\ctrong athư mục, trong khi trên Windows, nó luôn đề cập đến ctệp trong bthư mục con của athư mục. Vì vậy, khi cả dấu gạch chéo tiến và lùi được sử dụng trong một đường dẫn, bạn cần biết nền tảng liên quan để có thể diễn giải chính xác. Trong thực tế, thường an toàn khi cho rằng đó là đường dẫn cửa sổ vì dấu gạch chéo ngược hiếm khi được sử dụng trong tên tệp Linux, nhưng hãy ghi nhớ điều này khi bạn viết mã để bạn không tạo ra các lỗ hổng bảo mật ngẫu nhiên.


29
trên Windows, os.pathchỉ cần tải ntpathmô-đun bên trong. Sử dụng mô-đun này, có thể xử lý các '\\'dấu tách đường dẫn ngay cả trên các máy Linux. Đối với Linux, posixpathmô-đun (resp. os.path) Sẽ đơn giản hóa các hoạt động đường dẫn để chỉ cho phép các '/'dấu tách kiểu posix .
moooeeeep

@moooeeeep Vì vậy, chúng tôi có thể sử dụng câu trả lời của Stranac, và nó có đáng tin cậy không? ( "Sử dụng os.path.split hoặc os.path.basename như những người khác đề nghị sẽ không hoạt động trong mọi trường hợp: nếu bạn đang chạy tập lệnh trên Linux và cố xử lý đường dẫn kiểu cửa sổ cổ điển, nó sẽ thất bại" - - trích dẫn từ bài đăng của Lauritz - và tôi không hiểu, cảnh báo này có liên quan đến câu trả lời của Stranac hay không).
john cj

3
@ johnc.j Chỉ khi bạn cần phân tích các đường dẫn kiểu Windows (ví dụ r'C:\path\to\file.txt':) trên máy Linux, bạn mới cần sử dụng mô-đun ntpath. Nếu không, bạn có thể sử dụng các chức năng từ os.path. Điều này là do các hệ thống Linux thường cho phép sử dụng các ký tự dấu gạch chéo ngược trong tên tệp (như được giải thích trong câu trả lời).
moooeeeep

2
Không phải là giải pháp của bạn tương đương với os.path.basename(os.path.normpath(path))?
Mr_and_Mrs_D

2
Đối với những gì khách truy cập tương lai cho câu hỏi này, tôi đã gặp phải tình huống Lauritz đang cảnh báo và giải pháp của anh ta là giải pháp duy nhất có hiệu quả. Không có sự kết hợp với os có thể chỉ xuất ra tên tệp. Vì vậy, imho, ntpath là con đường để đi.
Harabeck

1250

Trên thực tế, có một hàm trả về chính xác những gì bạn muốn

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

22
Nếu bạn muốn xử lý các đường dẫn theo cách độc lập với hệ điều hành, thì đối với os.path.basename (u "C: \\ temp \\ bla.txt"), bạn đang mong đợi nhận được 'bla.txt'. Câu hỏi không phải là về việc có được một tên tệp hợp lệ, mà là trích xuất tên cho một đường dẫn.
Adi Roiban

3
Trong tìm kiếm Google của tôi để tìm tên tệp của một đường dẫn, câu trả lời này là hữu ích nhất. Trường hợp sử dụng của tôi chỉ có trên Windows.
Bobort

2
os.path.basename(your_path)Điều này đã làm việc! Tôi muốn đường dẫn kịch bản: os.path.dirname(os.path.realpath(__file__))và tên tập lệnh : os.path.basename(os.path.realpath(__file__)). Cảm ơn!
TheWalkingData

@AdiRoiban Bạn có thể vui lòng giải thích ý kiến ​​của bạn? Tôi đã thử nghiệm nó trên Windows 7 và tôi thực sự nhận được "bla.txt '. Nói đơn giản, tôi không thấy bất kỳ vấn đề nào (đối với bản thân tôi).
John cj

10
@ johnc.j Vấn đề là, khi bạn đã thử điều này trên Linux, bạn sẽ nhận được 'C:\\temp\\bla.txt'thay thế.
moooeeeep

218

os.path.split là chức năng bạn đang tìm kiếm

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

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

40
Chỉ cần người dùng cẩn thận, điều này sẽ trả về "" nếu đường dẫn kết thúc bằng "/" hoặc "\"
BuZz

Khi tôi thử "C: \ Users \ Dell \ Desktop \ ProjectShadow \ button \ button.py", nó sẽ trả về "ProjectShadow utton tton" cho mọi thứ ngoài điều này, nó trả về kết quả chính xác
amitnair92

4
@ amitnair92 - Hoặc thực hiện việc này: r "C: \ Users \ Dell \ Desktop \ ProjectShadow \ button \ button.py" hoặc điều này: "C: \\ Users \\ Dell \ Desktop \\ ProjectShadow \\ nút \\ .py "-" \ b "là một ký tự đặc biệt (hệ thống 'chuông' tôi nghĩ), tương tự như cách \ r hoặc \ n biểu thị sự trở lại dòng mới / vận chuyển. Tiền tố chuỗi với r "C: \ ..." có nghĩa là sử dụng đầu vào thô đã cho
Bruce Lamond

87

Trong trăn 3

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

3,4 đến 3,6 trở lên, tùy thuộc vào chính xác những mục pathlib bạn sử dụng.
LightCC

8
cũng có thể sử dụng Đường dẫn ("some / path / to / file.dat"). để lấy tên tệp mà không cần phần mở rộng tệp
s2t2

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

đuôi là những gì bạn muốn, tên tệp.

Xem tài liệu mô-đun os python để biết chi tiết


13
Chỉ cần người dùng cẩn thận, điều này sẽ trả về "" nếu đường dẫn kết thúc bằng "/" hoặc "\"
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

Trong ví dụ của bạn, bạn cũng sẽ cần phải cắt dấu gạch chéo từ bên phải để trả về c:

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

Cấp độ thứ hai:

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

cập nhật: Tôi nghĩ lazyrđã cung cấp câu trả lời đúng. Mã của tôi sẽ không hoạt động với các đường dẫn giống như windows trên các hệ thống unix và ngược lại với các đường dẫn giống như unix trên hệ thống windows.


Câu trả lời của bạn sẽ không hoạt động r"a\b\c"trên linux, cũng như "a/b/c"trên windows.
Lauritz V. Thaulow

Tất nhiên, os.path.basename(path)sẽ chỉ làm việc nếu os.path.isfile(path)True. Do đó, hoàn toàn path = 'a/b/c/'không phải là một tên tệp hợp lệ ...
moooeeeep

1
@fmaas os.path.basename hoàn toàn là một hàm xử lý chuỗi. Nó không quan tâm nếu tập tin tồn tại hoặc đó là một tập tin hoặc thư mục. os.path.basename("a/b/c/")trở lại ""vì dấu gạch chéo.
Lauritz V. Thaulow

lazyrbạn đúng rồi! Tôi đã không nghĩ về điều đó. Nó sẽ được an toàn để làm path = path.replace('\\', '/')?
Trượt tuyết

@Skirmantas Tôi cho rằng, nhưng nó không cảm thấy đúng. Tôi nghĩ rằng việc xử lý đường dẫn nên được thực hiện với các công cụ tích hợp được tạo ra cho công việc. Có rất nhiều thứ để đi hơn là bắt mắt.
Lauritz V. Thaulow

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

cái này sẽ trả về: paint.exe

thay đổi giá trị sep của hàm phân tách liên quan đến đường dẫn hoặc hệ điều hành của bạn.


Đây là câu trả lời tôi thích, nhưng tại sao không làm như sau? fname = str(path).split('/')[-1]
asultan904

10

Nếu bạn muốn lấy tên tệp tự động, bạn có thể làm

import glob

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

8

Nếu đường dẫn tệp của bạn không kết thúc bằng "/" và các thư mục được phân tách bằng "/" thì hãy sử dụng mã sau đây. Như chúng ta biết, đường dẫn thường không kết thúc bằng "/".

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

Nhưng trong một số trường hợp như URL kết thúc bằng "/" thì hãy sử dụng mã sau

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

nhưng khi đường dẫn của bạn được đặt bởi "\" mà bạn thường tìm thấy trong các đường dẫn windows thì bạn có thể sử dụng các mã sau

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

Bạn có thể kết hợp cả hai thành một chức năng bằng cách kiểm tra loại HĐH và trả về kết quả.


7

Điều này đang làm việc cho linux và windows cũng như với thư viện tiêu chuẩn

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]

Các kết quả:

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

6

Đây là một giải pháp chỉ dành cho regex, dường như hoạt động với mọi đường dẫn HĐH trên mọi HĐH.

Không có mô-đun nào khác là cần thiết và cũng không cần tiền xử lý:

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

Cập nhật:

Nếu bạn chỉ muốn một tên tệp tiềm năng , nếu có (nghĩa /a/b/là một thư mục và cũng vậy c:\windows\), hãy thay đổi biểu thức chính quy thành : r'[^\\/]+(?![\\/])$'. Đối với "regex bị thách thức", điều này sẽ thay đổi giao diện chuyển tiếp tích cực đối với một loại dấu gạch chéo thành một cái nhìn về phía trước tiêu cực, khiến các tên đường dẫn kết thúc bằng dấu gạch chéo không trả về gì thay vì thư mục con cuối cùng trong tên đường dẫn. Tất nhiên, không có gì đảm bảo rằng tên tệp tiềm năng thực sự đề cập đến một tệp và cho điều đó os.path.is_dir()hoặc os.path.is_file()sẽ cần phải được sử dụng.

Điều này sẽ phù hợp như sau:

/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 có thể được kiểm tra ở đây .


Bạn đang sử dụng re, tại sao không mô-đun os?
Saurabh Chandra Patel

@SaurabhChandraPatel đó là một thời gian dài. Nếu tôi nhớ chính xác, regex được sử dụng như một giải pháp đa nền tảng trong trường hợp này. Bạn có thể xử lý tên tệp windows trên máy chủ Linux chẳng hạn.
Eric Duminil

5

Có lẽ chỉ là tất cả trong một giải pháp của tôi mà không quan trọng một số giải pháp mới (liên quan đến tempfile để tạo các tệp tạm thời: D)

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

Lấy các giá trị của abc.namesẽ là một chuỗi như thế này: '/tmp/tmpks5oksk7' Vì vậy, tôi có thể thay thế /bằng một khoảng trắng .replace("/", " ")và sau đó gọi split(). Điều đó sẽ trả về một danh sách và tôi nhận được phần tử cuối cùng của danh sách với[-1]

Không cần phải có bất kỳ mô-đun nhập khẩu.


2
Nếu tên tệp hoặc thư mục chứa khoảng trắng thì sao?
kriss

1
Điều gì về một phân chia trực tiếp ("/") [- 1]?
Nan

4

Tôi chưa bao giờ thấy các đường dẫn bị gạch chéo kép, chúng có tồn tại không? Tính năng tích hợp của mô-đun python oskhông thành công cho những người đó. Tất cả những người khác làm việc, cũng là cảnh báo do bạn đưa ra với 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))

Đó không phải là gấp đôi backsmuses. Chúng là dấu gạch chéo ngược đơn và chúng cần được thoát.
Eric Duminil

3

Dấu phân cách Windows có thể ở tên tệp Unix hoặc Windows Path. Dấu tách Unix chỉ có thể tồn tại trong đường dẫn Unix. Sự hiện diện của dấu phân cách Unix biểu thị đường dẫn không phải của Windows.

Sau đây sẽ loại bỏ (cắt dấu phân cách) bằng dấu phân cách cụ thể của hệ điều hành, sau đó phân tách và trả về giá trị ngoài cùng bên phải. Nó xấu, nhưng đơn giản dựa trên giả định ở trên. Nếu giả định không chính xác, vui lòng cập nhật và tôi sẽ cập nhật phản hồi này để phù hợp với các điều kiện chính xác hơn.

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

mã mẫu:

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
Ngoài ra, vui lòng gửi cho tôi gợi ý về cách định dạng trong địa điểm này. Mất nửa tá cố gắng để có được mã mẫu tại chỗ.
dusc2don

1

Để hoàn thiện hơn, đây là pathlibgiải pháp cho 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']

Điều này hoạt động trên cả Windows và Linux.


1

Trong cả Python 2 và 3, sử dụng mô-đun 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)

Sử dụng:

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

Với testcase của bạn:

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

Ý tưởng ở đây là chuyển đổi tất cả các đường dẫn thành biểu diễn bên trong thống nhất pathlib2, với các bộ giải mã khác nhau tùy thuộc vào nền tảng. May mắn thay, pathlib2bao gồm một bộ giải mã chung được gọi là PurePathsẽ hoạt động trên bất kỳ đường dẫn nào. Trong trường hợp điều này không hoạt động, bạn có thể buộc nhận dạng đường dẫn windows bằng cách sử dụng fromwinpath=True. Điều này sẽ chia chuỗi đầu vào thành các phần, cái cuối cùng là chiếc lá bạn đang tìm kiếm, do đó path2unix(t)[-1].

Nếu đối số nojoin=False, đường dẫn sẽ được nối lại, do đó đầu ra chỉ đơn giản là chuỗi đầu vào được chuyển đổi sang định dạng Unix, có thể hữu ích để so sánh các đường dẫn phụ trên các nền tảng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.