【问题标题】:How can I replace (or strip) an extension from a filename in Python?如何在 Python 中替换(或去除)文件名的扩展名?
【发布时间】:2011-04-02 16:44:00
【问题描述】:

Python 中是否有一个内置函数可以替换(或删除)文件名的扩展名(如果有的话)?

例子:

print replace_extension('/home/user/somefile.txt', '.jpg')

在我的示例中:/home/user/somefile.txt 将变为 /home/user/somefile.jpg

我不知道这是否重要,但我需要这个用于我正在编写的 SCons 模块。 (所以也许有一些我可以使用的 SCons 特定功能?)

我想要干净的东西。对字符串中所有出现的.txt 进行简单的字符串替换显然是不干净的。 (如果我的文件名是somefile.txt.txt.txt,这将失败)

【问题讨论】:

  • SCons 允许在操作字符串中获取文件库。你能发布你需要这个的 scons 特定逻辑吗?这是用于动作、发射器、扫描仪吗?
  • 其中一些似乎不再起作用,因为路径返回的是 PosixPath 而不是字符串:p
  • Python 3.9 将允许path.removesuffix('.txt') + '.jpg',这可能是最简单的方法python.org/dev/peps/pep-0616

标签: python filenames scons strip


【解决方案1】:

试试os.path.splitext,它应该做你想做的事。

import os
print os.path.splitext('/home/user/somefile.txt')[0]+'.jpg'  # /home/user/somefile.jpg
os.path.splitext('/home/user/somefile.txt')  # returns ('/home/user/somefile', '.txt')

【讨论】:

  • 只有将新名称与 os.path.join 放在一起才能看起来干净。
  • @Tony Veijalainen:您不应该使用 os.path.join ,因为它用于将路径组件与特定于操作系统的路径分隔符连接起来。例如,print os.path.join(os.path.splitext('/home/user/somefile.txt')[0], '.jpg') 将返回 /home/user/somefile/.jpg,这是不可取的。
  • 显式优于隐式。如果有 0 个或 1 个后缀,则:pathlib.Path('/home/user/somefile.txt').with_suffix('.jpg')
  • 如果文件名中有多个点,则不会中断,例如如果文件名为some.file.txt?任何人都可以为这种情况提供解决方案吗?
  • @Garnagar 文件名 = 文件名.rsplit( ".", 2 )[ 0 ] + ".txt"
【解决方案2】:

扩展 AnaPana 的答案,如何使用pathlib (Python >= 3.4)删除扩展:

>>> from pathlib import Path

>>> filename = Path('/some/path/somefile.txt')

>>> filename_wo_ext = filename.with_suffix('')

>>> filename_replace_ext = filename.with_suffix('.jpg')

>>> print(filename)
/some/path/somefile.ext    

>>> print(filename_wo_ext)
/some/path/somefile

>>> print(filename_replace_ext)
/some/path/somefile.jpg

【讨论】:

  • Real Python 对 pathlib 模块的示例用例有很好的描述:realpython.com/python-pathlib
  • 这个答案是我的典型方法,但是当你有多个文件扩展名时它似乎失败了。例如,pth = Path('data/foo.tar.gz'); print(pth.with_suffix('.jpg')) 将输出 'data/foo.tar.jpg'。我想你可以做pth.with_suffix('').with_suffix('.jpg'),但它很笨重,你需要添加一个任意长的.with_suffix('')调用链,以便在文件扩展名中处理任意数量的点.(诚然,超过2 是一个奇异的边缘情况)。
  • @tel 你可以使用while 循环来解决这个问题:pth = Path('data/foo.tar.gz'); while pth != pth.with_suffix(''): pth = pth.with_suffix(''); pth = pth.with_suffix('.jpg')
  • 请参阅my answer below 了解多扩展问题的解决方案。
  • @tel:请注意,您的pth.with_suffix('').with_suffix('.jpg') 在一行中同时处理双后缀单后缀大小写。
【解决方案3】:

正如@jethro 所说,splitext 是一种巧妙的方法。但在这种情况下,您可以很容易地自行拆分,因为扩展名必须是文件名中最后一个句点之后的部分:

filename = '/home/user/somefile.txt'
print( filename.rsplit( ".", 1 )[ 0 ] )
# '/home/user/somefile'

rsplit 告诉 Python 从字符串的右侧开始执行字符串拆分,1 表示最多执行一次拆分(例如'foo.bar.baz' -> [ 'foo.bar', 'baz' ])。由于rsplit 将始终返回一个非空数组,我们可以安全地将0 索引到其中以获取文件名减去扩展名。

【讨论】:

  • 请注意,对于以点开头且没有其他扩展名的文件(如 Linux 上的隐藏文件,例如 .bashrc),使用 rsplit 将导致不同的结果。 os.path.splitext 为这些返回一个空扩展名,但使用 rsplit 会将整个文件名视为扩展名。
  • 这也会给文件名/home/john.johnson/somefile带来意想不到的结果
【解决方案4】:

我更喜欢使用str.rsplit() 的以下单行方法:

my_filename.rsplit('.', 1)[0] + '.jpg'

例子:

>>> my_filename = '/home/user/somefile.txt'
>>> my_filename.rsplit('.', 1)
>>> ['/home/user/somefile', 'txt']

【讨论】:

  • 如果 somefile 没有扩展名并且用户是 'john.doe',则会失败。
  • 他们不会都失败了吗?
【解决方案5】:

处理多个扩展

如果您有多个使用 pathlibstr.replace 的扩展程序,那就太好了:

删除/剥离扩展

>>> from pathlib import Path
>>> p = Path("/path/to/myfile.tar.gz")
>>> extensions = "".join(p.suffixes)

# any python version
>>> str(p).replace(extensions, "")
'/path/to/myfile'

# python>=3.9
>>> str(p).removesuffix(extensions)
'/path/to/myfile'

替换扩展

>>> p = Path("/path/to/myfile.tar.gz")
>>> extensions = "".join(p.suffixes)
>>> new_ext = ".jpg"
>>> str(p).replace(extensions, new_ext)
'/path/to/myfile.jpg'

如果您还想要一个pathlib 对象输出,那么您显然可以将这一行换成Path()

>>> Path(str(p).replace("".join(p.suffixes), ""))
PosixPath('/path/to/myfile')

将其全部封装在一个函数中

from pathlib import Path
from typing import Union

PathLike = Union[str, Path]


def replace_ext(path: PathLike, new_ext: str = "") -> Path:
    extensions = "".join(Path(path).suffixes)
    return Path(str(p).replace(extensions, new_ext))


p = Path("/path/to/myfile.tar.gz")
new_ext = ".jpg"

assert replace_ext(p, new_ext) == Path("/path/to/myfile.jpg")
assert replace_ext(str(p), new_ext) == Path("/path/to/myfile.jpg")
assert replace_ext(p) == Path("/path/to/myfile")
    

【讨论】:

  • pathlib 对此有一个快捷方式:Path().with_suffix("") 将删除扩展名,而 Path.with_suffix(".txt") 将替换它。
  • 正确。但它只删除了第一个扩展名。所以在上面的例子中,使用with_suffix 而不是replace 只会删除.gz 而不是.tar.gz 我的答案是“通用的”,但如果你只期望一个扩展名,with_suffix 将是更清洁的解决方案。
  • 从 Python 3.9 开始,您可以使用 removesuffix 而不是 replace。这可能更安全,例如在 Linux 上,一些 目录 可能有 .d 后缀:"/home/config.d/file.d".replace(".d", "") -> '/home/config/file'"/home/config.d/file.d".removesuffix(".d") -> '/home/config.d/file'。因此,还保存了"" 函数参数。
  • 感谢@AlexPovel 的提醒,我添加了一个使用removesuffix 用于python 3.9 的示例
【解决方案6】:

另一种方法是使用str.rpartition(sep) 方法。

例如:

filename = '/home/user/somefile.txt'
(prefix, sep, suffix) = filename.rpartition('.')

new_filename = prefix + '.jpg'

print new_filename

【讨论】:

    【解决方案7】:

    对于 Python >= 3.4:

    from pathlib import Path
    
    filename = '/home/user/somefile.txt'
    
    p = Path(filename)
    new_filename = p.parent.joinpath(p.stem + '.jpg') # PosixPath('/home/user/somefile.jpg')
    new_filename_str = str(new_filename) # '/home/user/somefile.jpg'
    

    【讨论】:

    • 我认为是 JS 建议的 pathlib 方法。更简单。
    【解决方案8】:

    TLDR: 在我看来,替换所有扩展的最佳方法如下。

    import pathlib
    p = pathlib.Path('/path/to.my/file.foo.bar.baz.quz')
    print(p.with_name(p.name.split('.')[0]).with_suffix('.jpg'))
    

    更长的答案: 执行此操作的最佳方法取决于您的 python 版本以及您需要处理多少扩展。也就是说,我很惊讶没有人提到 pathlib 的with_name。我还担心这里的某些答案无法处理父目录中的.。以下是完成扩展替换的几种方法。

    使用路径对象

    最多替换一个扩展

    import pathlib
    p = pathlib.Path('/path/to.my/file.foo')
    print(p.with_suffix('.jpg'))
    

    最多替换两个扩展

    import pathlib
    p = pathlib.Path('/path/to.my/file.foo.bar')
    print(p.with_name(p.stem).with_suffix('.jpg'))
    

    替换所有扩展

    使用 pathlibs with_name(我认为最好的解决方案):

    import pathlib
    p = pathlib.Path('/path/to.my/file.foo.bar.baz.quz')
    print(p.with_name(p.name.split('.')[0]).with_suffix('.jpg'))
    

    使用functools.reduce 和pathlib 的with_suffix

    import pathlib
    import functools
    p = pathlib.Path('/path/to.my/file.foo.bar.baz.quz')
    print(functools.reduce(lambda v, _: v.with_suffix(''), p.suffixes, p).with_suffix('.jpg'))
    print(functools.reduce(lambda v, e: v.with_suffix(e), ['' for _ in p.suffixes] + ['.jpg'], p))
    

    Python 3.9+ 使用 pathlib 和 str.removesuffix:

    import pathlib
    p = pathlib.Path('/path/to.my/file.foo.bar.baz.quz')
    print(pathlib.Path(str(p).removesuffix(''.join(p.suffixes))).with_suffix('.jpg'))
    

    不使用路径对象(仅限字符串)

    总的来说,我认为使用 pathlib 的解决方案更简洁,但不是每个人都能做到。如果您仍在使用 python 2,我很抱歉。如果没有python2的pathlib包,真的很抱歉。

    替换所有扩展

    Python 2.7 兼容使用os.path:

    import os
    ps = '/path/to.my/file.foo.bar.baz.quz'
    print(os.path.join(os.path.dirname(ps), os.path.basename(ps).split('.')[0] + '.jpg'))
    

    Python 3.9+ 使用removesuffixos.path(如果你有python3.9,为什么不使用pathlib?):

    import os
    ps = '/path/to.my/file.foo.bar.baz.quz'
    print(ps.removesuffix(os.path.splitext(ps)[-1].split('.', 1)[-1]) + 'jpg')
    

    【讨论】:

    • 这是我使用的,因为它似乎是这里不需要 python 3.9 的最佳答案
    • 谢谢。您让我查看了我的答案,修复了我的原始答案以处理两个以上的扩展,并添加了几个替代方案,包括 python3.9 答案。我不确定 3.9 是否真的让事情变得更简单。我错过了什么吗?
    • 您最后的 2.7 版本示例似乎已损坏,因为“ps.removesuffix”作为字符串方法仅在 3.9 版本中引入,并且两个答案完全相同。编辑错误?
    • @Welsige 谢谢。你是对的,我只是从我的测试文件中复制了错误的示例。现在应该可以工作了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多