从path中提取文件名,无论os/path格式如何

无论操作系统或路径格式如何,我都可以使用哪个Python库从路径中提取文件名?

例如,我希望所有这些路径返回我c

a/b/c/a/b/c\a\b\c\a\b\c\a\b\ca/b/../../a/b/c/a/b/../../a/b/c
1892558 次浏览

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

head, tail = os.path.split("/tmp/d/a.dat")
>>> print(tail)a.dat>>> print(head)/tmp/d
import oshead, tail = os.path.split('path/to/file.exe')

尾巴是你想要的,文件名。

查看详细信息python os模块文档

像其他人建议的那样使用os.path.splitos.path.basename在所有情况下都不起作用:如果您在Linux上运行脚本并尝试处理经典的windows样式路径,它将失败。

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

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

当然,如果文件以斜杠结尾,basename将为空,因此请创建自己的函数来处理它:

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文件名,但在编码时请记住这一点,以免造成意外的安全漏洞。

有一个函数返回你想要的

import osprint(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 LinuxType "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'

在您的示例中,您还需要从右侧去掉斜杠以返回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的路径。

也许只是我的所有在一个解决方案没有重要的一些新的(关于创建临时文件的临时文件:D)

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

获取abc.name的值将是这样的字符串:'/tmp/tmpks5oksk7'所以我可以用空格.replace("/", " ")替换/,然后调用split()。这将返回一个列表,我得到列表的最后一个元素[-1]

不需要导入任何模块。

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

这将返回:paint.exe

更改有关路径或操作系统的拆分函数的sep值。

我从未见过双反斜杠路径,它们存在吗?python模块os的内置功能不适用于这些。所有其他的工作,也是你在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))

这适用于linux和windows以及标准库

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

Windows分隔符可以位于Unix文件名或Windows路径中。Unix分隔符只能存在于Unix路径中。存在Unix分隔符表示非Windows路径。

下面将通过操作系统特定的分隔符剥离(切割尾随分隔符),然后拆分并返回最右边的值。它很丑陋,但基于上面的假设很简单。如果假设不正确,请更新,我会更新此响应以匹配更准确的条件。

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

示例代码:

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

这是一个仅正则表达式的解决方案,它似乎适用于任何操作系统上的任何操作系统路径。

不需要其他模块,也不需要预处理:

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

更新时间:

如果您只想要潜在文件名,如果存在(即/a/b/是dir,c:\windows\也是),请将正则表达式更改为:r'[^\\/]+(?![\\/])$'。对于“正则表达式挑战”,这将某种斜线的正前瞻更改为负前瞻,导致以斜线结尾的路径名不返回任何内容,而不是路径名中的最后一个子目录。当然,不能保证潜在文件名实际上引用了一个文件,并且需要使用os.path.is_dir()os.path.is_file()

这将匹配如下:

/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

正则表达式可以测试这里

为了完整起见,这里是Python 3.2+的pathlib解决方案:

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

这适用于Windows和Linux。

在python 3.4或更高版本中,使用#0

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

.name属性将给出路径中最后一个子元素的全名,无论它是文件还是文件夹。

在Python 2和3中,使用模块Pathlib2

import posixpath  # to generate unix pathsfrom pathlib2 import PurePath, PureWindowsPath, PurePosixPath
def path2unix(path, nojoin=True, fromwinpath=False):"""From a path given in any format, converts to posix path formatfromwinpath=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 pathif fromwinpath:pathparts = list(PureWindowsPath(path).parts)else:pathparts = list(PurePath(path).parts)if nojoin:return pathpartselse:return posixpath.join(*pathparts)

用法:

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 separatorsOut[11]: ['C:\\', 'lala', 'lolo', 'haha.dat']

使用您的测试用例:

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])...:...:ccccccc

这里的想法是将所有路径转换为pathlib2的统一内部表示,根据平台使用不同的解码器。幸运的是,pathlib2包括一个名为PurePath的通用解码器,它应该可以在任何路径上工作。如果这不起作用,你可以使用fromwinpath=True强制识别windows路径。这将把输入字符串分成几部分,最后一个是你要寻找的叶子,因此是path2unix(t)[-1]

如果参数nojoin=False,则路径将被连接回来,因此输出只是转换为Unix格式的输入字符串,这对于比较跨平台的子路径很有用。

如果您的文件路径不以“/”结尾,目录以“/”分隔,则使用以下代码。正如我们所知,路径通常不以“/”结尾。

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

但在某些情况下,例如URL以“/”结尾,然后使用以下代码

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

但是,当您的路径由“\”(通常在Windows路径中找到)划分时,您可以使用以下代码

import ospath_str = "c:\\var\www\index.html"print(os.path.basename(path_str))
import ospath_str = "c:\\home\some_str\last_str\\"split_path = path_str.rsplit("\\",1)print(os.path.basename(split_path[0]))

您可以通过检查OS类型并返回结果将两者组合成一个函数。

import osfile_location = '/srv/volume1/data/eds/eds_report.csv'file_name = os.path.basename(file_location )  #eds_report.csvlocation = os.path.dirname(file_location )    #/srv/volume1/data/eds

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

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

我个人最喜欢的是:

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

如果您在目录中有许多文件并希望将这些文件名存储到列表中。使用下面的代码。

import os as osimport glob as globpath = 'mypath'file_list= []for file in glob.glob(path):data_file_list = os.path.basename(file)file_list.append(data_file_list)

这是工作!

os.path.basename(name)

但是您无法在Windows文件路径Linux中获取文件名。Windows也是。os.path在不同的操作员系统上加载不同的模块:

  • Linux-位置路径
  • windows-npath

所以你可以使用os.path得到正确的结果总是

我在Windows和Ubuntu(WSL)上使用此方法,它的工作原理与(I)预期的一样,仅使用“导入os”:所以基本上,替换()根据您当前的操作系统平台放置正确的路径分离器。

如果路径以斜杠'/'结束,则它不是文件而是目录,因此它返回一个空字符串。

import os
my_fullpath = r"D:\MY_FOLDER\TEST\20201108\20201108_073751.DNG"os.path.basename(my_fullpath.replace('\\',os.sep))
my_fullpath = r"/MY_FOLDER/TEST/20201108/20201108_073751.DNG"os.path.basename(my_fullpath.replace('\\',os.sep))
my_fullpath = r"/MY_FOLDER/TEST/20201108/"os.path.basename(my_fullpath.replace('\\',os.sep))
my_fullpath = r"/MY_FOLDER/TEST/20201108"os.path.basename(my_fullpath.replace('\\',os.sep))

在Windows(左)和Ubuntu(通过WSL,右)上:输入图片描述

带扩展名的文件名

filepath = './dir/subdir/filename.ext'basename = os.path.basename(filepath)print(basename)# filename.ext
print(type(basename))# <class 'str'>

不带扩展名的文件名

basename_without_ext = os.path.splitext(os.path.basename(filepath))[0]print(basename_without_ext)# filename