如何在Python中从路径中获取没有扩展名的文件名?

1654

如何在Python中从路径中获取没有扩展名的文件名?

"/path/to/some/file.txt"  →  "file"

112
现代 Python 的正确答案是需要向下滚动一定的距离才能找到:from pathlib import Path; print(Path("/path/to/some/file.txt").stem) >>> file - BallpointBen
在这种情况下,您需要先解析出目录路径,然后再解析出没有扩展名的文件名。import os; filepath='./readme'; dir, filename_ext = os.path.split(filepath); filename=filename_ext.rsplit('.', maxsplit=1)[0]; print(f'{dir}/{filename}') ./readme - dlink
32个回答

1756

Python 3.4+

使用pathlib.Path.stem

>>> from pathlib import Path
>>> Path("/path/to/file.txt").stem
'file'
>>> Path("/path/to/file.tar.gz").stem
'file.tar'

Python < 3.4

使用os.path.splitextos.path.basename组合使用:

>>> os.path.splitext(os.path.basename("/path/to/file.txt"))[0]
'file'
>>> os.path.splitext(os.path.basename("/path/to/file.tar.gz"))[0]
'file.tar'

32
如果这是一个常见的操作,或许应该赋予它自己的官方命令?比如说 os.path.filename(path_to_file),而不是 os.path.splitext(os.path.basename(path_to_file))[0]。 - Fnord
28
如果文件名包含多个点号,该怎么办? - matteok
119
如果有人跟matteok一样想知道,如果有多个点,splitext函数会在最后一个点处拆分文件扩展名(例如splitext('kitty.jpg.zip')会返回 ('kitty.jpg', '.zip'))。 - Chuck
83
请注意,这段代码返回完整的文件路径(不包括扩展名),而不仅仅是文件名称 - Aran-Fey
18
是的,所以你需要执行 splitext(basename('/some/path/to/file.txt'))[0](我似乎总是这样做)。 - CpILL
显示剩余8条评论

1293

在Python 3.4+中,可以使用.stempathlib

from pathlib import Path

Path('/root/dir/sub/file.ext').stem

将返回

'file'

请注意,如果您的文件有多个扩展名,则.stem仅会删除最后一个扩展名。例如,Path('file.tar.gz').stem将返回'file.tar'

107
自 Python 3 以来,这是推荐的方式。 - Miladiouss
6
请注意,与os.path解决方案一样,这只会删除一个扩展名(或者pathlib称为suffix的内容)。例如,Path('a.b.c').stem == 'a.b' - BallpointBen
8
我认为反复调用 .with_suffix('') 是可行的方法。你可能需要循环,直到 p.suffix == '' - BallpointBen
4
对于具有复杂扩展名的文件,这种方法无法正常工作:pathlib.Path('backup.tar.gz').stem -> 'backup.tar 但期望结果为 backup - pymen
8
这取决于你如何定义“扩展名”。那么Fantastic Mr.Fox.mp4呢? - spectras
显示剩余11条评论

760
你可以使用以下代码创建自己的内容:
>>> import os
>>> base=os.path.basename('/root/dir/sub/file.ext')
>>> base
'file.ext'
>>> os.path.splitext(base)
('file', '.ext')
>>> os.path.splitext(base)[0]
'file'

重要提示:如果文件名中有多个 .,只有最后一个会被移除。例如:

/root/dir/sub/file.ext.zip -> file.ext

/root/dir/sub/file.ext.tar.gz -> file.ext.tar

请参阅下面其他回答的内容。


273
>>> print(os.path.splitext(os.path.basename("/path/to/file/hemanth.txt"))[0])
hemanth

9
+1。有完全相同的三个答案,但这个是最直接的答案。你可以使用\来显示代码,例如路径实例为"/somepath/hermanth.txt"。 - cregox
2
请注意,在您提供的语句中,“os.path.basename”是不必要的。应该只使用“os.path.basename”来从文件路径中获取文件名。 - arrt_

115

在Python 3.4+中,您可以使用pathlib解决方案

from pathlib import Path

print(Path(your_path).resolve().stem)

9
为什么要使用resolve()解析路径?如果不使用它,是否可能得到不包含文件名的路径?这意味着如果给定一个符号链接的路径,您将返回符号链接指向的文件的文件名(不带扩展名)。 - user3064538
3
使用 resolve() 的一个可能原因是帮助解决多个点的问题。如果路径为 "./foo.tar.gz",则下面关于使用索引的答案将不起作用。 - William Allcock

94

12
这是通用情况下删除完整路径中扩展名的最佳 Python 3 解决方案。使用"stem"方法也可以删除父路径。如果你期望有双重扩展名(例如bla.tar.gz),那么你甚至可以连续使用它两次:p.with_suffix('').with_suffix('')。 - Eelco van Vliet

40

正如@IceAdor在评论中提到@user2902201的解决方案,rsplit是最简单的解决方案之一,适用于多个句点(通过将分割数限制为从字符串结尾开始的maxsplit仅为1)。

这里是详细说明:

file = 'my.report.txt'
print file.rsplit('.', maxsplit=1)[0]

my.report


如果没有扩展名的文件位于名称中带有点(s)的目录中,例如./readme,则此方法将失败。 - Wolf

31

如果扩展名中有多个点,os.path.splitext()将无法正常工作。

例如,images.tar.gz

>>> import os
>>> file_path = '/home/dc/images.tar.gz'
>>> file_name = os.path.basename(file_path)
>>> print os.path.splitext(file_name)[0]
images.tar
你可以找到基本名称中第一个句点的索引,然后切片基本名称以获取没有扩展名的文件名。
>>> import os
>>> file_path = '/home/dc/images.tar.gz'
>>> file_name = os.path.basename(file_path)
>>> index_of_dot = file_name.index('.')
>>> file_name_without_extension = file_name[:index_of_dot]
>>> print file_name_without_extension
images

1
获取文件名后,将会执行 index_of_dot = file_name.index('.') 以避免在 .env 处分割。 - Dheeraj Chakravarthi
2
重要的一点是,这样的扩展名序列是常见的,比如 ".tar.gz"、".tar.bz"、".tar.7z"。 - user6798019
3
注意:如果在“haystack”中找不到针(在上面的例子中是点号.),则'haystack'.index('needle')会引发ValueError异常。也存在没有任何扩展名的文件。 - Czechnology
为解决这个问题,可以使用try-catch,或者使用str.find()并检查是否为-1。如果没有点,则只需返回file_name - Starwarswii

31
如果您想保留文件路径并仅删除扩展名
>>> file = '/root/dir/sub.exten/file.data.1.2.dat'
>>> print ('.').join(file.split('.')[:-1])
/root/dir/sub.exten/file.data.1.2

22
如果你想在最后一个句点处分割字符串,可以使用rsplit函数:'/root/dir/sub.exten/file.data.1.2.dat'.rsplit('.', 1) - IceArdor

26

使用Pathlib回答几个场景

使用Pathlib,当只有一个扩展名(或没有)时获取文件名非常简单,但处理多个扩展名的一般情况可能会很棘手。

零个或一个扩展名

from pathlib import Path

pth = Path('./thefile.tar')

fn = pth.stem

print(fn)      # thefile


# Explanation:
# the `stem` attribute returns only the base filename, stripping
# any leading path if present, and strips the extension after
# the last `.`, if present.


# Further tests

eg_paths = ['thefile',
            'thefile.tar',
            './thefile',
            './thefile.tar',
            '../../thefile.tar',
            '.././thefile.tar',
            'rel/pa.th/to/thefile',
            '/abs/path/to/thefile.tar']

for p in eg_paths:
    print(Path(p).stem)  # prints thefile every time

两个或更少的扩展名

from pathlib import Path

pth = Path('./thefile.tar.gz')

fn = pth.with_suffix('').stem

print(fn)      # thefile


# Explanation:
# Using the `.with_suffix('')` trick returns a Path object after
# stripping one extension, and then we can simply use `.stem`.


# Further tests

eg_paths += ['./thefile.tar.gz',
             '/abs/pa.th/to/thefile.tar.gz']

for p in eg_paths:
    print(Path(p).with_suffix('').stem)  # prints thefile every time

任意数量的扩展(0、1或更多)

from pathlib import Path

pth = Path('./thefile.tar.gz.bz.7zip')

fn = pth.name
if len(pth.suffixes) > 0:
    s = pth.suffixes[0]
    fn = fn.rsplit(s)[0]

# or, equivalently

fn = pth.name
for s in pth.suffixes:
    fn = fn.rsplit(s)[0]
    break

# or simply run the full loop

fn = pth.name
for _ in pth.suffixes:
    fn = fn.rsplit('.')[0]

# In any case:

print(fn)     # thefile


# Explanation
#
# pth.name     -> 'thefile.tar.gz.bz.7zip'
# pth.suffixes -> ['.tar', '.gz', '.bz', '.7zip']
#
# If there may be more than two extensions, we can test for
# that case with an if statement, or simply attempt the loop
# and break after rsplitting on the first extension instance.
# Alternatively, we may even run the full loop and strip one 
# extension with every pass.


# Further tests

eg_paths += ['./thefile.tar.gz.bz.7zip',
             '/abs/pa.th/to/thefile.tar.gz.bz.7zip']

for p in eg_paths:
    pth = Path(p)
    fn = pth.name
    for s in pth.suffixes:
        fn = fn.rsplit(s)[0]
        break

    print(fn)  # prints thefile every time

已知第一个扩展名的特殊情况

例如,如果扩展名可能是.tar.tar.gz.tar.gz.bz等,则可以简单地rsplit已知的扩展名并取第一个元素:


pth = Path('foo/bar/baz.baz/thefile.tar.gz')

fn = pth.name.rsplit('.tar')[0]

print(fn)      # thefile

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