Ich habe eine Zeichenfolge, die ich als Dateinamen verwenden möchte, daher möchte ich mit Python alle Zeichen entfernen, die in Dateinamen nicht zulässig sind.
Ich bin lieber streng als sonst, also sagen wir, ich möchte nur Buchstaben, Ziffern und eine kleine Menge anderer Zeichen wie behalten "_-.() "
. Was ist die eleganteste Lösung?
Der Dateiname muss auf mehreren Betriebssystemen (Windows, Linux und Mac OS) gültig sein. Es handelt sich um eine MP3-Datei in meiner Bibliothek mit dem Songtitel als Dateiname, die von drei Computern gemeinsam genutzt und gesichert wird.
os.path
lädt je nach Betriebssystem tatsächlich eine andere Bibliothek (siehe den zweiten Hinweis in der Dokumentation ). Wenn also eineos.path
Anführungszeichenfunktion implementiert wurde , konnte die Zeichenfolge nur für die POSIX-Sicherheit beim Ausführen auf einem POSIX-System oder für die Windows-Sicherheit beim Ausführen unter Windows angegeben werden. Der resultierende Dateiname wäre nicht unbedingt sowohl für Windows als auch für POSIX gültig, was in der Frage gefordert wird.Antworten:
Sie können sich das Django-Framework ansehen, um herauszufinden, wie sie aus beliebigem Text einen "Slug" erstellen. Ein Slug ist URL- und Dateinamen-freundlich.
Die Django-Text-Utils definieren eine Funktion,
slugify()
das ist wahrscheinlich der Goldstandard für solche Dinge. Im Wesentlichen lautet ihr Code wie folgt.Es gibt noch mehr, aber ich habe es weggelassen, da es nicht um Slugification geht, sondern um Flucht.
quelle
value
. Wenn der Wert Unicode sein muss, müssen Sie sicherstellen, dass es sich tatsächlich um Unicode handelt. Oder. Möglicherweise möchten Sie die Unicode-Normalisierung weglassen, wenn Ihr tatsächlicher Wert tatsächlich eine ASCII-Zeichenfolge ist.slugify
Funktion wurde nach django / utils / text.py verschoben , und diese Datei enthält auch eineget_valid_filename
Funktion.Dieser Whitelist-Ansatz (dh es werden nur die in valid_chars vorhandenen Zeichen zugelassen) funktioniert, wenn die Formatierung der Dateien oder die Kombination gültiger Zeichen, die unzulässig sind (wie z. B. ".."), nicht eingeschränkt ist würde einen Dateinamen mit dem Namen ". txt" zulassen, der meiner Meinung nach unter Windows nicht gültig ist. Da dies der einfachste Ansatz ist , würde ich versuchen , Leerzeichen aus dem valid_chars zu entfernen und eine bekannte gültige Zeichenfolge im Fehlerfall voranstellen, wird jeder anderer Ansatz wissen, was erlaubt ist , wo zu bewältigen Windows - Dateibenennung Einschränkungen und damit sein viel komplexer.
quelle
valid_chars = frozenset(valid_chars)
würde nicht schaden. Es ist 1,5-mal schneller, wenn es auf Allchars angewendet wird."CON"
unter Windows Sie in Schwierigkeiten bringen wird ...Sie können das Listenverständnis zusammen mit den Zeichenfolgenmethoden verwenden.
quelle
filename = "".join(i for i in s if i not in "\/:*?<>|")
"".join( x for x in s if (x.isalnum() or x in "._- "))
Was ist der Grund, die Zeichenfolgen als Dateinamen zu verwenden? Wenn die menschliche Lesbarkeit kein Faktor ist, würde ich mich für das base64-Modul entscheiden, das dateisystemsichere Zeichenfolgen erzeugen kann. Es ist nicht lesbar, aber Sie müssen sich nicht mit Kollisionen befassen und es ist reversibel.
Update : Geändert basierend auf Matthews Kommentar.
quelle
your_string
muss ein Byte-Array sein oder das Ergebnis davon,encode('ascii')
damit dies funktioniert.def url2filename(url): url = url.encode('UTF-8') return base64.urlsafe_b64encode(url).decode('UTF-8') def filename2url(f): return base64.urlsafe_b64decode(f).decode('UTF-8')
Um die Sache noch weiter zu verkomplizieren, wird nicht garantiert, dass Sie einen gültigen Dateinamen erhalten, indem Sie ungültige Zeichen entfernen. Da sich zulässige Zeichen in verschiedenen Dateinamen unterscheiden, kann ein konservativer Ansatz dazu führen, dass aus einem gültigen Namen ein ungültiger wird. Möglicherweise möchten Sie eine spezielle Behandlung für die folgenden Fälle hinzufügen:
Die Zeichenfolge besteht nur aus ungültigen Zeichen (Sie haben eine leere Zeichenfolge).
Sie erhalten eine Zeichenfolge mit einer besonderen Bedeutung, z. B. "." oder ".."
Unter Windows sind bestimmte Gerätenamen reserviert. Beispielsweise können Sie keine Datei mit den Namen "nul", "nul.txt" (oder nul.anything in der Tat) erstellen. Die reservierten Namen sind:
CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8 und LPT9
Sie können diese Probleme wahrscheinlich umgehen, indem Sie den Dateinamen, die in einem dieser Fälle niemals auftreten können, eine Zeichenfolge voranstellen und ungültige Zeichen entfernen.
quelle
Auf Github gibt es ein schönes Projekt namens python-slugify :
Installieren:
Dann benutze:
quelle
test.txt
bekommttest-txt
was zu viel ist.Genau wie S.Lott geantwortet hat, können Sie im Django Framework nachsehen , wie eine Zeichenfolge in einen gültigen Dateinamen konvertiert wird.
Die neueste und aktualisierte Version befindet sich in utils / text.py und definiert "get_valid_filename" wie folgt:
(Siehe https://github.com/django/django/blob/master/django/utils/text.py )
quelle
django.utils.text import get_valid_filename
re.sub(r'(?u)[^-\w.]', '', s)
nicht vertraut sind, werden alle Zeichen entfernt, die keine Buchstaben, keine Zahlen (0-9), kein Unterstrich ('_'), kein Bindestrich ('-') und kein Punkt ('.' Sind.) ). "Buchstaben" enthält hier alle Unicode-Buchstaben, z. B. 漢語.Dies ist die Lösung, die ich letztendlich verwendet habe:
Der Aufruf unicodedata.normalize ersetzt Zeichen mit Akzent durch das Äquivalent ohne Akzent, was besser ist, als sie einfach zu entfernen. Danach werden alle nicht erlaubten Zeichen entfernt.
Meine Lösung stellt keine bekannte Zeichenfolge voran, um mögliche unzulässige Dateinamen zu vermeiden, da ich weiß, dass sie aufgrund meines speziellen Dateinamenformats nicht auftreten können. Eine allgemeinere Lösung müsste dies tun.
quelle
Beachten Sie, dass es auf Unix-Systemen keine anderen Einschränkungen für Dateinamen gibt als
Alles andere ist Freiwild.
Ja, ich habe gerade ANSI-Farbcodes in einem Dateinamen gespeichert und sie wirksam werden lassen.
Fügen Sie zur Unterhaltung ein BEL-Zeichen in einen Verzeichnisnamen ein und beobachten Sie den Spaß, der entsteht, wenn Sie eine CD hineinlegen;)
quelle
In einer Zeile:
Sie können auch das Zeichen '_' eingeben, um die Lesbarkeit zu verbessern (z. B. beim Ersetzen von Schrägstrichen).
quelle
Sie können die Methode re.sub () verwenden, um alles zu ersetzen, was nicht "filelike" ist. Tatsächlich könnte jedoch jedes Zeichen gültig sein. Es gibt also keine vorgefertigten Funktionen (glaube ich), um dies zu erreichen.
Würde zu einem Dateihandle zu /tmp/filename.txt führen.
quelle
Es werden keine leeren Zeichenfolgen, speziellen Dateinamen ('nul', 'con' usw.) verarbeitet.
quelle
Obwohl du vorsichtig sein musst. In Ihrem Intro wird nicht klar gesagt, ob Sie nur die lateinische Sprache betrachten. Einige Wörter können bedeutungslos werden oder eine andere Bedeutung, wenn Sie sie nur mit ASCII-Zeichen bereinigen.
Stellen Sie sich vor, Sie haben "forêt poésie" (Waldpoesie), Ihre Desinfektion könnte "fort-posie" (stark + etwas Bedeutungsloses) ergeben.
Schlimmer noch, wenn Sie sich mit chinesischen Schriftzeichen auseinandersetzen müssen.
"下 北 沢" Ihr System führt möglicherweise "---" aus, was nach einer Weile zum Scheitern verurteilt und nicht sehr hilfreich ist. Wenn Sie sich also nur mit Dateien befassen, würde ich empfehlen, sie entweder als generische Kette zu bezeichnen, die Sie steuern, oder die Zeichen so zu belassen, wie sie sind. Für URIs ungefähr gleich.
quelle
Warum nicht einfach das "osopen" mit einem Versuch / Ausnahme abschließen und das zugrunde liegende Betriebssystem aussortieren lassen, ob die Datei gültig ist?
Dies scheint viel weniger Arbeit zu sein und gilt unabhängig davon, welches Betriebssystem Sie verwenden.
quelle
osopen
Ausführung auf einem Computer nicht erkennen können .Ein weiteres Problem, das in den anderen Kommentaren noch nicht behandelt wurde, ist die leere Zeichenfolge, bei der es sich offensichtlich nicht um einen gültigen Dateinamen handelt. Sie können auch eine leere Zeichenfolge erhalten, wenn Sie zu viele Zeichen entfernen.
Was ist mit den von Windows reservierten Dateinamen und Problemen mit Punkten die sicherste Antwort auf die Frage "Wie normalisiere ich einen gültigen Dateinamen aus willkürlichen Benutzereingaben?" ist "nicht einmal die Mühe machen": Wenn Sie einen anderen Weg finden, dies zu vermeiden (z. B. die Verwendung ganzzahliger Primärschlüssel aus einer Datenbank als Dateinamen), tun Sie dies.
Wenn Sie müssen, und Sie müssen wirklich Leerzeichen und '.' Versuchen Sie für Dateierweiterungen als Teil des Namens Folgendes:
Selbst dies kann nicht richtig garantiert werden, insbesondere auf unerwarteten Betriebssystemen - zum Beispiel hasst RISC OS Leerzeichen und verwendet '.' als Verzeichnistrennzeichen.
quelle
Ich mochte den Python-Slugify-Ansatz hier, aber er entfernte auch Punkte, was nicht erwünscht war. Also habe ich es für das Hochladen eines sauberen Dateinamens auf s3 wie folgt optimiert:
Beispielcode:
Ausgabe:
Dies ist so ausfallsicher, dass es mit Dateinamen ohne Erweiterung funktioniert und sogar nur mit Dateinamen für unsichere Zeichen (Ergebnis
none
hier).quelle
Antwort geändert für Python 3.6
quelle
Mir ist klar, dass es viele Antworten gibt, aber sie basieren meistens auf regulären Ausdrücken oder externen Modulen. Deshalb möchte ich meine eigene Antwort einbringen. Eine reine Python-Funktion, kein externes Modul erforderlich, kein regulärer Ausdruck verwendet. Mein Ansatz ist nicht, ungültige Zeichen zu bereinigen, sondern nur gültige zuzulassen.
Wenn Sie möchten, können Sie der
validchars
Variablen zu Beginn Ihre eigenen gültigen Zeichen hinzufügen , z. B. Ihre nationalen Buchstaben, die im englischen Alphabet nicht vorhanden sind. Dies ist möglicherweise etwas, das Sie möchten oder nicht möchten: Einige Dateisysteme, die nicht unter UTF-8 ausgeführt werden, haben möglicherweise immer noch Probleme mit Nicht-ASCII-Zeichen.Diese Funktion dient zum Testen der Gültigkeit eines einzelnen Dateinamens, sodass Pfadtrennzeichen durch _ ersetzt werden, wobei ungültige Zeichen berücksichtigt werden. Wenn Sie dies hinzufügen möchten, ist es trivial, das
if
Trennzeichen für den OS-Pfad zu ändern .quelle
Die meisten dieser Lösungen funktionieren nicht.
'/ hallo / welt' -> 'helloworld'
'/ helloworld' / -> 'helloworld'
Dies ist nicht das, was Sie im Allgemeinen wollen. Angenommen, Sie speichern das HTML für jeden Link. Sie werden das HTML für eine andere Webseite überschreiben.
Ich nehme ein Diktat wie:
2 steht für die Nummer, die an den nächsten Dateinamen angehängt werden soll.
Ich schaue jedes Mal nach dem Dateinamen aus dem Diktat. Wenn es nicht vorhanden ist, erstelle ich ein neues und füge bei Bedarf die maximale Anzahl hinzu.
quelle
Nicht genau das, wonach OP gefragt hat, aber das verwende ich, weil ich eindeutige und reversible Konvertierungen benötige:
Das Ergebnis ist "etwas" lesbar, zumindest aus Sicht des Systemadministrators.
quelle
def safe_filename(filename): return safePath(filename.strip().replace(' ','_'))
Wenn es Ihnen nichts ausmacht, ein Paket zu installieren, sollte dies hilfreich sein: https://pypi.org/project/pathvalidate/
Von https://pypi.org/project/pathvalidate/#sanitize-a-filename :
quelle
Ich bin mir sicher, dass dies keine gute Antwort ist, da es die Zeichenfolge ändert, über die es sich dreht, aber es scheint in Ordnung zu funktionieren:
quelle
"".join( x for x in s if (x.isalnum() or x in "._- "))
in diesem Beitrag Kommentare gefundenAKTUALISIEREN
Alle Links sind in dieser 6 Jahre alten Antwort irreparabel gebrochen.
Außerdem würde ich es auch nicht mehr so machen, nur
base64
unsichere Zeichen verschlüsseln oder löschen. Beispiel für Python 3:Mit können
base64
Sie codieren und decodieren, sodass Sie den ursprünglichen Dateinamen wieder abrufen können.Abhängig vom Anwendungsfall ist es jedoch möglicherweise besser, einen zufälligen Dateinamen zu generieren und die Metadaten in einer separaten Datei oder Datenbank zu speichern.
ORIGINAL LINKROTTEN ANTWORT :
Das
bobcat
Projekt enthält ein Python-Modul, das genau dies tut.Es ist nicht ganz robust, siehe diesen Beitrag und diese Antwort .
Wie bereits erwähnt:
base64
Codierung ist wahrscheinlich eine bessere Idee, wenn die Lesbarkeit keine Rolle spielt.quelle