Как заменить (или удалить) расширение из имени файла в Python?

114

Есть ли в Python встроенная функция, которая могла бы заменить (или удалить что угодно) расширение имени файла (если оно есть)?

Пример:

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

В моем примере: /home/user/somefile.txtстанет/home/user/somefile.jpg

Не знаю, имеет ли это значение, но мне это нужно для модуля SCons, который я пишу. (Так, может быть, я могу использовать какую-то особую функцию SCons?)

Я хочу что-нибудь чистое . Совершенно .txtочевидно, что простая замена всех вхождений внутри строки не является чистой. (Это не сработает, если мое имя файла somefile.txt.txt.txt)

ereOn
источник
SCons позволяет получить файловую базу в строке действия. Можете ли вы опубликовать конкретную логику вашего бра, которая в этом нуждается? Это для экшена, эмиттера, сканера?
bdbaddog
некоторые из них, похоже, больше не работают, поскольку path возвращает PosixPath, а не строку: p
shigeta

Ответы:

148

Попробуйте os.path.splitext, он должен делать то, что вы хотите.

import os
print os.path.splitext('/home/user/somefile.txt')[0]+'.jpg'
джетро
источник
15
@ S.Lott: Верьте мне или нет. Но я сделал. Я всегда делаю. Возможно, с неправильными условиями.
ereOn
@ereOn: Поскольку в вашем вопросе используется почти такая же формулировка, я немного удивлен, что вы не нашли его. Ваш вопрос состоит из 5 слов подряд, которые точно совпадают.
S.Lott
Добавляйте новое имя вместе с os.path.join, чтобы оно выглядело чистым.
Тони Вейялайнен
4
@Tony Veijalainen: Вы не должны использовать os.path.join, потому что он предназначен для объединения компонентов пути с разделителем пути для конкретной ОС. Например, print os.path.join(os.path.splitext('/home/user/somefile.txt')[0], '.jpg')вернется /home/user/somefile/.jpg, что нежелательно.
scottclowe 07
@ S.Lott - 99 человек, проголосовавших за этот ответ, довольно явно означают, что этот пост полезен, не нужно
стыдить
92

Расширяя ответ 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
JS.
источник
1
В Real Python есть хорошее описание примеров использования модуля pathlib
Стивен К. Хауэлл
2
Это мой типичный подход, но, похоже, он не работает, когда у вас есть несколько расширений файлов. Например, 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')
dericke
См. Мой ответ ниже, чтобы узнать о решении проблемы с несколькими расширениями.
Майкл Холл,
33

Как сказал @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его, чтобы получить имя файла без расширения.

Катриэль
источник
8
Обратите внимание, что использование rsplitприведет к разным результатам для файлов, которые начинаются с точки и не имеют другого расширения (например, скрытые файлы в Linux .bashrc). os.path.splitextвозвращает для них пустое расширение, но при использовании rsplitвсе имя файла рассматривается как расширение.
Флориан Брукер,
4
Это также даст неожиданные результаты для имени файла/home/john.johnson/somefile
Уилл Мэнли
7

Я предпочитаю следующий однострочный подход с использованием str.rsplit () :

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

Пример:

>>> my_filename = '/home/user/somefile.txt'
>>> my_filename.rsplit('.', 1)
>>> ['/home/user/somefile', 'txt']
ИванД
источник
2
Это не сработает, если файл somefile не имеет расширения, а пользователь - john.doe.
Марек
Разве тогда все они не потерпят неудачу?
eatmeimadanish
6

Для 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'
АнаПана
источник
1
Я думаю, что подход pathlib, предложенный JS. намного проще.
h0b0
4

Обработка нескольких расширений

В случае, если у вас есть несколько расширений, этот однострочник использует pathlibи str.replaceработает:

Удалить / удалить расширения

>>> from pathlib import Path
>>> p = Path("/path/to/myfile.tar.gz")
>>> str(p).replace("".join(p.suffixes), "")
'/path/to/myfile'

Заменить расширения

>>> p = Path("/path/to/myfile.tar.gz")
>>> new_ext = ".jpg"
>>> str(p).replace("".join(p.suffixes), 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это будет более чистым решением.
Майкл Холл,
3

Другой способ - использовать этот str.rpartition(sep)метод.

Например:

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

new_filename = prefix + '.jpg'

print new_filename
user2802945
источник