从路径中提取文件名,无论操作系统/路径格式如何。

1298
哪个Python库可以用来从路径中提取文件名,无论操作系统或路径格式如何?
例如,我想让所有这些路径都返回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

4
因为答案中的方法已经过时,所以需要更新。标准库:pathlib.Path就是为此而设计的。 - ninMonkey
1
这个问题太疯狂了。在所有操作系统上,没有一种“可靠”的方法来解析同时包含正斜杠和反斜杠的路径。在Unix系统中,文件夹名称中可以包含反斜杠。你只能实现一个“大部分时间都有效”的解决方案,也就是说会有bug。最好找到一种避免使用这种疯狂路径的方法。使用系统库来解析路径,并且在构建路径时也要使用系统库。解决这个问题的最佳方案是消除这种模棱两可的路径。祝你好运! - Roland
1
作为@Roland所说的一个例子:在Linux上\a\b\c是一个有效的文件名。而仅返回c可能是无效和危险的。 - undefined
23个回答

1850

有一个函数可以完全返回你想要的内容

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

警告: 在 POSIX 系统上使用os.path.basename()从Windows样式的路径(例如"C:\\my\\file.txt")获取基本名称时,将返回整个路径。

以下示例是在运行在Linux主机上的交互式Python shell:

Python 3.8.2 (default, Mar 13 2020, 10:14:16)
[GCC 9.3.0] on Linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> filepath = "C:\\my\\path\\to\\file.txt" # A Windows style file path.
>>> os.path.basename(filepath)
'C:\\my\\path\\to\\file.txt'

37
如果您希望以操作系统无关的方式处理路径,那么对于os.path.basename(u"C:\temp\bla.txt"),您期望得到'bla.txt'。问题不在于获取有效的文件名,而是提取路径的名称。 - Adi Roiban
4
在我的谷歌搜索中,寻找路径的文件名,这个答案是最有帮助的。无论如何,我的使用情况只限于Windows操作系统。 - Bobort
10
os.path.basename(your_path) 运行成功了!我想要脚本路径:os.path.dirname(os.path.realpath(__file__)) 和脚本名称:os.path.basename(os.path.realpath(__file__))。谢谢! - TheWalkingData
16
重点是,当你尝试在 Linux 上执行此操作时,结果会变成 'C:\temp\bla.txt'。 - moooeeeep
4
你说得对,Linux实现这一点非常自我中心,不能将反斜杠视为正确的路径分隔符。好消息是,在Linux上,Windows风格的路径确实能够正常工作,但你必须只使用___正斜杠___(所以你可以使用 filepath.replace('\\', '/') 来获得跨平台的兼容性)。 - bobobobo
显示剩余4条评论

1053

正如其他人所建议的使用os.path.splitos.path.basename并不适用于所有情况:如果您在Linux上运行脚本并尝试处理典型的Windows风格路径,则会失败。

Windows路径可以使用反斜杠或正斜杠作为路径分隔符。因此,ntpath模块(在Windows上运行时相当于os.path)将适用于所有平台上的所有路径(1)

import ntpath
ntpath.basename("a/b/c")
当然,如果文件名以斜杠结尾,则基本名称为空,因此您需要编写自己的函数来处理它:
def path_leaf(path):
    head, tail = ntpath.split(path)
    return tail or ntpath.basename(head)

验证:

>>> 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) 注意:Linux 文件名可以包含反斜杠。因此,在 Linux 上,r'a/b\c' 总是指向 a 文件夹中的 b\c 文件,而在 Windows 上,则总是指向 a 文件夹中的 b 子文件夹中的 c 文件。因此,当路径中同时使用正斜杠和反斜杠时,您需要知道相关的平台才能正确解释它。在实践中,通常可以安全地假定它是 Windows 路径,因为在 Linux 文件名中很少使用反斜杠,但编写代码时请记住这一点,以免创建意外的安全漏洞。


38
在Windows上,os.path 内部加载 ntpath 模块。使用这个模块,甚至可以在Linux系统上处理 '\\' 路径分隔符。对于Linux系统,posixpath 模块(或者 os.path)将简化路径操作以仅允许posix风格的 '/' 分隔符。 - moooeeeep
2
@moooeeeep 所以我们可以使用Stranac的答案,它是可靠的吗?(“像其他人建议的使用os.path.split或os.path.basename在所有情况下都不起作用:如果您在Linux上运行脚本并尝试处理经典的Windows风格路径,它将失败”--引用来自Lauritz的帖子--我不明白,这个警告是否涉及Stranac的答案)。 - john c. j.
6
只有在Linux机器上需要解析Windows风格的路径(例如r'C:\path\to\file.txt')时,你才需要使用ntpath模块。否则,你可以使用os.path中的函数。这是因为Linux系统通常允许在文件名中使用反斜杠字符(如答案所述)。 - moooeeeep
2
你的解决方案不就等同于 os.path.basename(os.path.normpath(path)) 吗? - Mr_and_Mrs_D
4
对于未来查看此问题的访问者而言,我遇到了Lauritz所警告的情况,他的解决方案是唯一有效的。通过对操作系统进行调整无法仅输出文件名,因此在我看来,使用ntpath是正确的方法。 - Harabeck
显示剩余3条评论

387

os.path.split是你要找的函数。

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

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

62
为了让其他用户注意,如果路径以 "/" 或 "" 结尾,这将返回 ""。 - BuZz
当我尝试运行"C:\Users\Dell\Desktop\ProjectShadow\button\button.py"时,除此之外的所有内容都会返回"ProjectShadowuttontton",而对于这个文件则会返回正确的结果。 - amitnair92
6
@amitnair92 - 你可以选择这样做:r"C:\Users\Dell\Desktop\ProjectShadow\button\button.py" 或者这样做:"C:\Users\Dell\Desktop\ProjectShadow\button\button.py"。“\b”是一个特殊字符(我认为是系统中的“响铃”),类似于\r或\n表示换行/回车。在字符串前面添加r"C:..."表示使用给定的原始输入。 - Bruce Lamond

313
在Python 3.4或更高版本中,使用pathlib.Path
>>> from pathlib import Path    
>>> Path("/tmp/d/a.dat").name
'a.dat'

.name 属性将返回路径中最后一个子元素的完整名称,无论它是文件还是文件夹。


1
取决于您使用的确切路径项,需要将版本从3.4升级到3.6或更高版本。 - LightCC
82
可以使用 Path("some/path/to/file.dat").stem 来获取文件名(不包括扩展名)。 - s2t2
4
pathlib.Path('some/path/to/file.dat').suffix 则返回文件扩展名。 - squarespiral

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

你想要的是文件名,使用 tail。

详见 Python os 模块文档


21
为了让其他用户注意,如果路径以 "/" 或 "" 结尾,这将返回 ""。 - BuZz

42
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

回答既甜美又简洁!谢谢! - Stefan

20

我个人最喜欢的是:

filename = fullname.split(os.sep)[-1]

17

如果你想自动获取文件名,可以这样做

import glob

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

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

这将返回:paint.exe

根据您的路径或操作系统更改split函数的分隔符值。


3
我喜欢这个答案,但为什么不直接执行以下操作呢? fname = str(path).split('/')[-1] - asultan904

15
在您的示例中,您还需要从右侧剥离斜杠以返回c
>>> import os
>>> path = 'a/b/c/'
>>> path = path.rstrip(os.sep) # strip the slash from the right side
>>> os.path.basename(path)
'c'

第二级:

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

更新:我认为lazyr提供了正确的答案。我的代码在Unix系统上将无法使用类似Windows的路径,反之亦然,在Windows系统上也无法使用类Unix的路径。


你的答案在Linux上无法处理r"a\b\c",在Windows上也无法处理"a/b/c" - Lauritz V. Thaulow
当然,只有当 os.path.isfile(path)True 时,os.path.basename(path) 才能正常使用。因此,path = 'a/b/c/' 根本不是一个有效的文件名... - moooeeeep
1
@fmaas os.path.basename是一种纯字符串处理函数。它不关心文件是否存在或者它是一个文件还是目录。因为末尾有斜杠,所以os.path.basename("a/b/c/")返回"" - Lauritz V. Thaulow
lazyr 你说得对!我没有考虑到那个。只做 path = path.replace('\\', '/') 安全吗? - Ski
@Skirmantas 我认为可以,但感觉不太对。我认为路径处理应该使用为此工作而设计的内置工具完成。关于路径的内容比表面上看到的要多得多。这里有更多相关信息。 - Lauritz V. Thaulow
的确,我认为lazyr有最强大的功能 ;) - BuZz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接