Jak zamienić (lub usunąć) rozszerzenie z nazwy pliku w Pythonie?

113

Czy w Pythonie jest funkcja wbudowana, która zastąpiłaby (lub usunęłaby cokolwiek) rozszerzenie nazwy pliku (jeśli takie ma)?

Przykład:

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

W moim przykładzie: /home/user/somefile.txtstałoby się/home/user/somefile.jpg

Nie wiem, czy to ma znaczenie, ale potrzebuję tego dla modułu SCons, który piszę. (Więc może jest jakaś specyficzna funkcja SCons, której mogę użyć?)

Chciałbym coś czystego . Wykonanie prostej zamiany ciągu wszystkich wystąpień .txtw ciągu nie jest oczywiście czyste. (To się nie powiedzie, jeśli moja nazwa pliku to somefile.txt.txt.txt)

ereOn
źródło
SCons umożliwia dostęp do bazy plików w ciągu akcji. Czy możesz opublikować swoją specyficzną logikę, która tego potrzebuje? Czy to dla akcji, nadajnika, skanera?
bdbaddog
niektóre z nich wydają się już nie działać, ponieważ path zwraca PosixPath, a nie ciąg: p
shigeta

Odpowiedzi:

146

Wypróbuj os.path.splitext , powinien zrobić to, co chcesz.

import os
print os.path.splitext('/home/user/somefile.txt')[0]+'.jpg'
jethro
źródło
15
@ S.Lott: Wierz mi lub nie. Ale to zrobiłem. Zawsze to robię. Być może z niewłaściwymi warunkami.
ereOn
@ereOn: Ponieważ twoje pytanie zawiera prawie takie same sformułowania, jestem trochę zaskoczony, że go nie znalazłeś. Twoje pytanie zawiera 5 słów - z rzędu - które dokładnie pasują.
S.Lott
Dodaj nową nazwę tylko razem z os.path.join, aby wyglądała na czystą.
Tony Veijalainen
4
@Tony Veijalainen: Nie powinieneś używać os.path.join, ponieważ służy to do łączenia komponentów ścieżki z separatorem ścieżki specyficznym dla systemu operacyjnego. Na przykład print os.path.join(os.path.splitext('/home/user/somefile.txt')[0], '.jpg')zwróci /home/user/somefile/.jpg, co nie jest pożądane.
scottclowe,
@ S.Lott - 99 osób się prawa głosu tę odpowiedź dość wyraźnie oznacza ten post jest pomocny, nie ma potrzeby wszystkich kapitalizacji zawstydzanie
JeffThompson
92

Rozwijając odpowiedź AnaPana, jak usunąć rozszerzenie za pomocą 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.
źródło
1
Real Python ma dobry opis przykładowych przypadków użycia modułu pathlib
Steven C. Howell
2
Ta odpowiedź jest moim typowym podejściem, ale wydaje się, że zawodzi, gdy masz wiele rozszerzeń plików. Na przykład pth = Path('data/foo.tar.gz'); print(pth.with_suffix('.jpg'))wyświetli 'data/foo.tar.jpg'. Przypuszczam, że możesz to zrobić pth.with_suffix('').with_suffix('.jpg'), ale jest to niezgrabne i musiałbyś dodać dowolnie długi łańcuch .with_suffix('')wywołań, aby poradzić sobie z dowolną liczbą kropek .w rozszerzeniu pliku ( trzeba przyznać, że więcej niż 2 to egzotyczny przypadek krawędzi).
tel
@tel Możesz użyć whilepętli, aby rozwiązać ten problem:pth = Path('data/foo.tar.gz'); while pth != pth.with_suffix(''): pth = pth.with_suffix(''); pth = pth.with_suffix('.jpg')
dericke
Zobacz moją odpowiedź poniżej, aby znaleźć rozwiązanie problemu z wieloma rozszerzeniami.
Michael Hall
33

Jak powiedział @jethro, splitextjest to zgrabny sposób na zrobienie tego. Ale w tym przypadku dość łatwo jest to podzielić samodzielnie, ponieważ rozszerzenie musi być częścią nazwy pliku znajdującą się po ostatnim okresie:

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

rsplitMówi Python wykonać szpagat ciągów zaczynając od prawej strony napisu, a 1mówi się wykonywać co najwyżej jeden split (tak że np 'foo.bar.baz'-> [ 'foo.bar', 'baz' ]). Ponieważ rsplitzawsze zwróci niepustą tablicę, możemy bezpiecznie zindeksować 0do niej, aby uzyskać nazwę pliku bez rozszerzenia.

Katriel
źródło
8
Zauważ, że użycie rsplitspowoduje różne wyniki dla plików, które zaczynają się od kropki i nie mają innego rozszerzenia (jak np .bashrc. Pliki ukryte w Linuksie ). os.path.splitextzwraca dla nich puste rozszerzenie, ale użycie rsplitpotraktuje całą nazwę pliku jako rozszerzenie.
Florian Brucker,
4
Daje to również nieoczekiwane wyniki dla nazwy pliku/home/john.johnson/somefile
Will Manley
7

Preferuję następujące jednoliniowe podejście przy użyciu str.rsplit () :

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

Przykład:

>>> my_filename = '/home/user/somefile.txt'
>>> my_filename.rsplit('.', 1)
>>> ['/home/user/somefile', 'txt']
IvanD
źródło
2
To się nie powiedzie, jeśli plik nie ma rozszerzenia, a użytkownik to „john.doe”.
Marek Jedliński
Czy wtedy wszyscy nie zawiodą?
eatmeimadanish
6

Dla Pythona> = 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'
AnaPana
źródło
1
Myślę, że podejście pathlib sugerowane przez JS. jest znacznie prostsze.
h0b0
4

Obsługa wielu rozszerzeń

W przypadku, gdy masz wiele rozszerzeń, ta jedna linijka używa pathlibi str.replacedziała:

Usuń / zdejmij rozszerzenia

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

Wymień rozszerzenia

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

Jeśli chcesz również uzyskać pathlibobiekt wyjściowy, możesz oczywiście zawinąć linięPath()

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

Zawijanie tego wszystkiego w funkcję

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')
Michael Hall
źródło
pathlib ma do tego skrót: Path (). with_suffix ("") usunie rozszerzenie, a Path.with_suffix (". txt") zastąpi je.
Levi
Poprawny. Ale usuwa tylko pierwsze rozszerzenie. Tak więc w powyższym przykładzie użycie with_suffixzamiast replaceusuwałoby tylko .gzzamiast. .tar.gz Moja odpowiedź miała być „ogólna”, ale jeśli spodziewasz się tylko jednego rozszerzenia, with_suffixbyłoby czystszym rozwiązaniem.
Michael Hall
3

Innym sposobem jest użycie str.rpartition(sep)metody.

Na przykład:

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

new_filename = prefix + '.jpg'

print new_filename
user2802945
źródło