273

Is there a built-in function in Python that would replace (or remove, whatever) the extension of a filename (if it has one)?

Example:

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

In my example: /home/user/somefile.txt would become /home/user/somefile.jpg

I don't know if it matters, but I need this for a SCons module I'm writing. (So perhaps there is some SCons specific function I can use ?)

I'd like something clean. Doing a simple string replacement of all occurrences of .txt within the string is obviously not clean. (This would fail if my filename is somefile.txt.txt.txt)

6
  • 2
    possible duplicate of Extracting extension from filename in Python
    – S.Lott
    Commented Aug 23, 2010 at 15:55
  • SCons allows getting at the filebase in an action string. Can you post your scons specific logic that needs this? Is this for the action, emitter, scanner?
    – bdbaddog
    Commented Nov 1, 2015 at 3:00
  • some of this doesn't seem to work any more as path returns a PosixPath not a string :p
    – shigeta
    Commented Oct 10, 2019 at 18:04
  • 13
    Python 3.9 will allow path.removesuffix('.txt') + '.jpg', which will likely be the easiest way going forward python.org/dev/peps/pep-0616
    – panofsteel
    Commented Oct 1, 2020 at 0:20
  • @panofsteel What import is needed for that?
    – not2qubit
    Commented Nov 21, 2022 at 16:49

8 Answers 8

319

Try os.path.splitext it should do what you want.

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')
5
  • 1
    Only put the new name together with os.path.join to look clean. Commented Aug 23, 2010 at 17:51
  • 10
    @Tony Veijalainen: You shouldn't use os.path.join because that is for joining path components with the OS-specific path separator. For example, print os.path.join(os.path.splitext('/home/user/somefile.txt')[0], '.jpg') will return /home/user/somefile/.jpg, which is not desirable.
    – scottclowe
    Commented Jan 7, 2016 at 0:37
  • 4
    Explict is better than implicit. If there is zero or one suffix then: pathlib.Path('/home/user/somefile.txt').with_suffix('.jpg') Commented May 5, 2021 at 20:40
  • 1
    Won't this break if there is more than one dot in the file name, e.g. if the file is called some.file.txt? Can anyone provide solution for this case?
    – Garnagar
    Commented Jan 5, 2022 at 14:07
  • 1
    @Garnagar filename = filename.rsplit( ".", 2 )[ 0 ] + ".txt"
    – eramm
    Commented Mar 16, 2022 at 10:29
257

Expanding on AnaPana's answer, how to remove an extension using 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.txt    

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

>>> print(filename_replace_ext)
/some/path/somefile.jpg
5
  • 2
    Real Python has a good write-up of example use cases of the pathlib module: realpython.com/python-pathlib Commented Oct 16, 2018 at 13:24
  • 3
    This answer is my typical approach, but it seems to fail when you have multiple file extensions. For example, pth = Path('data/foo.tar.gz'); print(pth.with_suffix('.jpg')) will output 'data/foo.tar.jpg'. I suppose you can do pth.with_suffix('').with_suffix('.jpg'), but it's clunky, and you would need to add an arbitrarily long chain of .with_suffix('') calls in order to deal with an arbitrary number of dots . in a file extension (admittedly, more than 2 is an exotic edge case).
    – tel
    Commented May 28, 2019 at 2:50
  • 2
    @tel You could use a while loop to solve that: pth = Path('data/foo.tar.gz'); while pth != pth.with_suffix(''): pth = pth.with_suffix(''); pth = pth.with_suffix('.jpg')
    – dericke
    Commented May 20, 2020 at 22:15
  • See my answer below for a solution to the multiple extensions problem. Commented Jun 8, 2020 at 2:05
  • 1
    @tel: Note that your pth.with_suffix('').with_suffix('.jpg') handles both the double suffix and the single suffix case, in one line.
    – djvg
    Commented Feb 14, 2022 at 21:17
63

As @jethro said, splitext is the neat way to do it. But in this case, it's pretty easy to split it yourself, since the extension must be the part of the filename coming after the final period:

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

The rsplit tells Python to perform the string splits starting from the right of the string, and the 1 says to perform at most one split (so that e.g. 'foo.bar.baz' -> [ 'foo.bar', 'baz' ]). Since rsplit will always return a non-empty array, we may safely index 0 into it to get the filename minus the extension.

3
  • 15
    Note that using rsplit will result in different results for files which start with a dot and have no other extension (like hidden files on Linux, e.g. .bashrc). os.path.splitext returns an empty extension for these, but using rsplit will treat the whole filename as an extension. Commented Jan 24, 2012 at 11:11
  • 11
    This will also give unexpected results for the filename /home/john.johnson/somefile Commented Nov 13, 2016 at 10:26
  • not to mention cases where you have filename.original.xml
    – tonysepia
    Commented Aug 22, 2023 at 12:00
24

I prefer the following one-liner approach using str.rsplit():

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

Example:

>>> my_filename = '/home/user/somefile.txt'
>>> my_filename.rsplit('.', 1)
>>> ['/home/user/somefile', 'txt']
2
  • 5
    This fails if the somefile has no extension and user is 'john.doe'. Commented May 18, 2017 at 9:05
  • 3
    Wouldn't they all fail then? Commented Apr 30, 2019 at 22:41
19

Handling multiple extensions

In the case where you have multiple extensions using pathlib and str.replace works a treat:

Remove/strip extensions

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

Replace extensions

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

If you also want a pathlib object output then you can obviously wrap the line in Path()

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

Wrapping it all up in a function

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")
4
  • 4
    pathlib has a shortcut for this: Path().with_suffix("") will remove an extension and Path.with_suffix(".txt") will replace it.
    – Levi
    Commented Jun 7, 2020 at 19:51
  • 6
    Correct. But it only removes the first extension. So in the above example, using with_suffix instead of replace would only remove .gz instead of .tar.gz My answer was intended to be "general", but if you only expect a single extension, with_suffix would be a cleaner solution. Commented Jun 8, 2020 at 2:00
  • 3
    Fittingly, from Python 3.9 onward, you can use removesuffix over replace. This is perhaps safer, e.g. on Linux some directories might have a .d suffix: "/home/config.d/file.d".replace(".d", "") -> '/home/config/file' versus "/home/config.d/file.d".removesuffix(".d") -> '/home/config.d/file'. So, also saves the "" function argument.
    – Alex Povel
    Commented Nov 29, 2020 at 11:21
  • 1
    Thanks for the heads up @AlexPovel, I have added an example using removesuffix for python 3.9 Commented Dec 1, 2020 at 1:52
9

TLDR: Best way to replace all extensions, in my opinion, is the following.

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

Longer Answer: The best way to do this will depend on your version of python and how many extensions you need to handle. That said, I'm surprised nobody has mentioned pathlib's with_name. I'm also concerned that some answers here don't handle a . in the parent directories. Here are several ways to accomplish extension replacement.

Using Path Objects

Replace Up to One Extension

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

Replace Up to Two Extensions

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

Replace All Extensions

Using pathlibs with_name (best solution, in my opinion):

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

Using functools.reduce and pathlib's 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+ Using pathlib and 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'))

Without Using Path Objects (Strings Only)

In general, I think solutions using pathlib are cleaner, but not everybody can do that. If you're still using python 2, I'm sorry. If you don't have the pathlib package for python2, I'm really sorry.

Replace All Extensions

Python 2.7 compatible using 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+ Using removesuffix and os.path (if you have python3.9, why aren't you using pathlib?):

import os
ps = '/path/to.my/file.foo.bar.baz.quz'
print(ps.removesuffix(os.path.splitext(ps)[-1].split('.', 1)[-1]) + 'jpg')
5
  • This is what I used, as it seems to be the best answer here that doesn't need python 3.9 Commented Jul 27, 2021 at 17:11
  • Thanks. You caused me review my answer, fix my original answer to handle more than two extensions, and add several alternatives, including python3.9 answers. I'm not sure that 3.9 actually makes things any simpler. Am I missing something?
    – izzy18
    Commented Jul 27, 2021 at 21:37
  • 1
    Your last 2.7 version example seems broken, as "ps.removesuffix" as a string method was only introduced with 3.9 version, and both answers are exactly equal. Edit error?
    – Welsige
    Commented Sep 8, 2021 at 13:11
  • @Welsige Thanks. You're right, I just copied over the wrong example from my test file. Should work now.
    – izzy18
    Commented Sep 8, 2021 at 17:23
  • Please don't forget to also show the output from each of your suggested solutions.
    – not2qubit
    Commented Nov 21, 2022 at 16:39
8

Another way to do is to use the str.rpartition(sep) method.

For example:

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

new_filename = prefix + '.jpg'

print new_filename
7

For 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'
2
  • 4
    I think the pathlib approach suggested by JS. is much simpler.
    – h0b0
    Commented Aug 17, 2017 at 9:47
  • Agreed .stem is the simplist answer
    – M__
    Commented May 25, 2023 at 3:53

Not the answer you're looking for? Browse other questions tagged or ask your own question.