Wie ersetze (oder entferne) ich eine Erweiterung von einem Dateinamen in Python?

110

Gibt es in Python eine integrierte Funktion, die die Erweiterung eines Dateinamens (falls vorhanden) ersetzt (oder entfernt)?

Beispiel:

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

In meinem Beispiel: /home/user/somefile.txtwürde werden/home/user/somefile.jpg

Ich weiß nicht, ob es wichtig ist, aber ich brauche dies für ein SCons-Modul, das ich schreibe. (Vielleicht gibt es einige SCons-spezifische Funktionen, die ich verwenden kann?)

Ich hätte gerne etwas Sauberes . Ein einfacher String-Austausch aller Vorkommen .txtinnerhalb des Strings ist offensichtlich nicht sauber. (Dies würde fehlschlagen, wenn mein Dateiname ist somefile.txt.txt.txt)

ereOn
quelle
Mit SCons können Sie in einer Aktionszeichenfolge auf die Dateibasis zugreifen. Können Sie Ihre Scons-spezifische Logik veröffentlichen, die dies benötigt? Ist das für die Aktion, Emitter, Scanner?
Bdbaddog
Einiges davon scheint nicht mehr zu funktionieren, da der Pfad einen PosixPath zurückgibt, keinen String: p
shigeta

Antworten:

146

Versuchen Sie os.path.splitext, es sollte tun, was Sie wollen.

import os
print os.path.splitext('/home/user/somefile.txt')[0]+'.jpg'
Jethro
quelle
15
@ S.Lott: Glaub mir oder nicht. Hab ich doch. Mache ich immer. Vielleicht mit den falschen Begriffen.
Ereon
@ereOn: Da Ihre Frage fast genau die gleiche Formulierung verwendet, bin ich ein wenig überrascht, dass Sie sie nicht gefunden haben. Ihre Frage enthält 5 Wörter hintereinander, die genau übereinstimmen.
S.Lott
Fügen Sie den neuen Namen nur zusammen mit os.path.join ein, um sauber auszusehen.
Tony Veijalainen
4
@ Tony Veijalainen: Sie sollten os.path.join nicht verwenden, da dies zum Verbinden von Pfadkomponenten mit dem betriebssystemspezifischen Pfadtrennzeichen dient. Zum Beispiel print os.path.join(os.path.splitext('/home/user/somefile.txt')[0], '.jpg')wird zurückkehren /home/user/somefile/.jpg, was nicht wünschenswert ist.
Scottclowe
@ S.Lott - 99 Leute stimmen diese Antwort ziemlich deutlich ab, was bedeutet, dass dieser Beitrag hilfreich ist, keine Notwendigkeit für All-Caps-
Scham
90

Erweitern Sie die Antwort von AnaPana, wie Sie eine Erweiterung mit pathlib entfernen (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.
quelle
1
Real Python hat eine gute Beschreibung von Beispielanwendungsfällen des Pathlib-Moduls: realpython.com/python-pathlib
Steven C. Howell
2
Diese Antwort ist mein typischer Ansatz, scheint jedoch zu scheitern, wenn Sie mehrere Dateierweiterungen haben. pth = Path('data/foo.tar.gz'); print(pth.with_suffix('.jpg'))Wird zum Beispiel ausgegeben 'data/foo.tar.jpg'. Ich nehme an, Sie können dies tun pth.with_suffix('').with_suffix('.jpg'), aber es ist umständlich, und Sie müssten eine beliebig lange Kette von .with_suffix('')Aufrufen hinzufügen , um mit einer beliebigen Anzahl von Punkten .in einer Dateierweiterung umgehen zu können (zugegebenermaßen ist mehr als 2 ein exotischer Randfall).
Tel
@tel Sie könnten eine whileSchleife verwenden, um das zu lösen:pth = Path('data/foo.tar.gz'); while pth != pth.with_suffix(''): pth = pth.with_suffix(''); pth = pth.with_suffix('.jpg')
dericke
In meiner Antwort unten finden Sie eine Lösung für das Problem mit mehreren Erweiterungen.
Michael Hall
32

Wie @jethro sagte, splitextist der ordentliche Weg, es zu tun. In diesem Fall ist es jedoch ziemlich einfach, es selbst aufzuteilen, da die Erweiterung der Teil des Dateinamens sein muss, der nach dem letzten Zeitraum kommt:

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

Das rsplitweist Python an, die Zeichenfolgensplits beginnend rechts von der Zeichenfolge 1durchzuführen , und das sagt, dass höchstens eine Teilung durchgeführt werden soll (z . B. 'foo.bar.baz'-> [ 'foo.bar', 'baz' ]). Da rsplitimmer ein nicht leeres Array zurückgegeben wird, können wir sicher darin indizieren 0, um den Dateinamen abzüglich der Erweiterung zu erhalten.

Katriel
quelle
8
Beachten Sie, dass die Verwendung rsplitzu unterschiedlichen Ergebnissen für Dateien führt, die mit einem Punkt beginnen und keine andere Erweiterung haben (wie z .bashrc. B. versteckte Dateien unter Linux ). os.path.splitextGibt für diese eine leere Erweiterung zurück, bei Verwendung rsplitwird jedoch der gesamte Dateiname als Erweiterung behandelt.
Florian Brucker
4
Dies wird auch unerwartete Ergebnisse für den Dateinamen/home/john.johnson/somefile
Will Manley
7

Ich bevorzuge den folgenden Einzeiler -Ansatz mit str.rsplit () :

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

Beispiel:

>>> my_filename = '/home/user/somefile.txt'
>>> my_filename.rsplit('.', 1)
>>> ['/home/user/somefile', 'txt']
IvanD
quelle
2
Dies schlägt fehl, wenn die Somefile keine Erweiterung hat und der Benutzer 'john.doe' ist.
Marek Jedliński
Würden sie dann nicht alle scheitern?
Eatmeimadanish
6

Für 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'
AnaPana
quelle
1
Ich denke, der von JS vorgeschlagene Pathlib-Ansatz. ist viel einfacher.
h0b0
4

Umgang mit mehreren Erweiterungen

Für den Fall, dass Sie mehrere Erweiterungen haben, verwendet dieser Einzeiler pathlibund str.replacefunktioniert ein Vergnügen:

Verlängerungen entfernen / entfernen

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

Erweiterungen ersetzen

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

Wenn Sie auch eine pathlibObjektausgabe wünschen , können Sie die Zeile natürlich einschließenPath()

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

Alles in eine Funktion einwickeln

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
quelle
pathlib hat eine Verknüpfung dafür: Path (). with_suffix ("") entfernt eine Erweiterung und Path.with_suffix (". txt") ersetzt sie.
Levi
Richtig. Es wird jedoch nur die erste Erweiterung entfernt. Im obigen Beispiel sollte die Verwendung von with_suffixanstelle von replacenur .gzanstelle von .tar.gz Meine Antwort "allgemein" sein. Wenn Sie jedoch nur eine einzige Erweiterung erwarten, with_suffixwäre dies eine sauberere Lösung.
Michael Hall
3

Eine andere Möglichkeit ist die Verwendung der str.rpartition(sep)Methode.

Beispielsweise:

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

new_filename = prefix + '.jpg'

print new_filename
user2802945
quelle