454 Stimmen

Umwandeln Sie eine Zeichenfolge in einen gültigen Dateinamen?

Ich habe einen String, den ich als Dateinamen verwenden möchte, daher möchte ich alle Zeichen entfernen, die in Dateinamen nicht erlaubt wären, mit Python.

Ich wäre lieber streng als nachlässig, also möchte ich nur Buchstaben, Zahlen und eine kleine Menge anderer Zeichen wie "_-.() " behalten. Was ist die eleganteste Lösung?

Der Dateiname muss auf mehreren Betriebssystemen gültig sein (Windows, Linux und Mac OS) - es handelt sich um eine MP3-Datei in meiner Bibliothek mit dem Songtitel als Dateiname und wird zwischen 3 Maschinen gemeinsam genutzt und gesichert.

35 Stimmen

Sollte dies nicht in das os.path-Modul integriert sein?

5 Stimmen

Vielleicht, obwohl ihr Anwendungsfall einen einzigen sicheren Pfad über alle Plattformen erfordern würde, nicht nur die aktuelle, wofür das Betriebssystem nicht konzipiert ist, um damit umzugehen.

6 Stimmen

Um den obigen Kommentar zu erweitern: Das aktuelle Design von os.path lädt tatsächlich je nach Betriebssystem eine andere Bibliothek (siehe die zweite Notiz in der Dokumentation). Wenn eine Quoting-Funktion in os.path implementiert wäre, könnte sie nur das Zeichenfolge für POSIX-Sicherheit quoten, wenn sie auf einem POSIX-System ausgeführt wird, oder für Windows-Sicherheit, wenn sie auf Windows ausgeführt wird. Der resultierende Dateiname wäre nicht unbedingt gültig für sowohl Windows als auch POSIX, was die Frage verlangt.

51voto

cowlinator Punkte 5083

Genau wie S.Lott beantwortet hat, können Sie sich das Django Framework anschauen, wie sie einen String in einen gültigen Dateinamen umwandeln.

Die neueste und aktualisierte Version befindet sich in utils/text.py und definiert get_valid_filename, das wie folgt aussieht:

def get_valid_filename(name):
    s = str(name).strip().replace(" ", "_")
    s = re.sub(r"(?u)[^-\w.]", "", s)
    if s in {"", ".", ".."}:
        raise SuspiciousFileOperation("Could not derive file name from '%s'" % name)
    return s

( Siehe https://github.com/django/django/blob/master/django/utils/text.py )

8 Stimmen

Für die Faulen, die schon auf Django sind: django.utils.text import get_valid_filename

3 Stimmen

In diesem Fall, wenn Ihnen regex nicht vertraut ist, wird mit re.sub(r'(?u)[^-\w.]', '', s) alle Zeichen entfernt, die keine Buchstaben, keine Zahlen (0-9), kein Unterstrich ('_'), kein Bindestrich ('-') und kein Punkt ('.') sind. "Buchstaben" beinhaltet hier alle Unicode-Buchstaben, wie z. B. .

5 Stimmen

Du könntest auch die Länge überprüfen: Dateinamen sind auf 255 Zeichen begrenzt (oder, weißt du, 32; je nach Dateisystem)

48voto

Brian Punkte 112487

Nur um die Dinge weiter zu komplizieren, ist es nicht garantiert, dass Sie einen gültigen Dateinamen erhalten, indem Sie ungültige Zeichen entfernen. Da erlaubte Zeichen bei verschiedenen Dateinamen variieren, könnte ein konservativer Ansatz dazu führen, dass ein gültiger Name in einen ungültigen umgewandelt wird. Sie möchten möglicherweise eine spezielle Behandlung für die Fälle hinzufügen, in denen:

  • Der String nur aus ungültigen Zeichen besteht (und Sie mit einem leeren String zurücklassen)

  • Sie einen String mit einer besonderen Bedeutung erhalten, z.B. "." oder ".."

  • Auf Windows sind bestimmte Gerätenamen reserviert. Sie können zum Beispiel keine Datei mit dem Namen "nul", "nul.txt" (oder nul.irgendwas) 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 einen String voranstellen, der niemals zu einem dieser Fälle führen kann, und ungültige Zeichen entfernen.

20voto

mnach Punkte 452

In einer Zeile:

valid_file_name = re.sub('[^\w_.)( -]', '', any_string)

Sie können auch das '_' Zeichen hinzufügen, um es lesbarer zu machen (zum Beispiel beim Ersetzen von Schrägstrichen)

20voto

Sophie Gage Punkte 4762

Dies ist die Lösung, die ich letztendlich verwendet habe:

import unicodedata

validFilenameChars = "-_.() %s%s" % (string.ascii_letters, string.digits)

def removeDisallowedFilenameChars(filename):
    cleanedFilename = unicodedata.normalize('NFKD', filename).encode('ASCII', 'ignore')
    return ''.join(c for c in cleanedFilename if c in validFilenameChars)

Der Aufruf von unicodedata.normalize ersetzt akzentuierte Zeichen durch das unbetonte Äquivalent, was besser ist als sie einfach zu entfernen. Danach werden alle nicht erlaubten Zeichen entfernt.

Meine Lösung fügt keinen bekannten String voran, um mögliche nicht erlaubte Dateinamen zu vermeiden, da ich weiß, dass sie in meinem speziellen Dateinamenformat nicht auftreten können. Eine allgemeinere Lösung müsste dies tun.

1 Stimmen

Du solltest uuid.uuid4() für deinen eindeutigen Präfix verwenden können

11 Stimmen

Kamel Schreibweise .. ahh

0 Stimmen

Könnte dies bearbeitet / aktualisiert werden, um mit Python 3.6 zu funktionieren?

16voto

Kent Fredric Punkte 55042

Denken Sie daran, dass auf Unix-Systemen tatsächlich nur wenige Einschränkungen für Dateinamen gelten, außer

  • Es darf kein \0 enthalten
  • Es darf kein / enthalten

Alles andere ist erlaubt.

$ touch "
> sogar mehrzeilig
> haha
> ^\[\[31m rot ^\[\[0m
> böse"
$ ls -la 
-rw-r--r--       0 Nov 17 23:39 ?sogar mehrzeilig?haha??\[31m rot ?\[0m?böse
$ ls -lab
-rw-r--r--       0 Nov 17 23:39 \\nsogar\\ mehrzeilig\\nhaha\\n\\033\[31m\\ rot\\ \\033\[0m\\nböse
$ perl -e 'for my $i ( glob(q{./\*sogar\*}) ){ print $i; } '
./
sogar mehrzeilig
haha
 rot 
böse

Ja, ich habe tatsächlich ANSI-Farbcodes in einem Dateinamen gespeichert und sie wirken lassen.

Zur Unterhaltung, fügen Sie einem Verzeichnisnamen ein BEL-Zeichen hinzu und beobachten Sie den Spaß, der entsteht, wenn Sie dorthin wechseln ;)

0 Stimmen

Der OP gibt an, dass "Der Dateiname auf mehreren Betriebssystemen gültig sein muss".

1 Stimmen

@cowlinator Diese Klarstellung wurde 10 Stunden nach meinem Beitrag hinzugefügt :) Überprüfen Sie das Bearbeitungsprotokoll des OP.

0 Stimmen

\0 scheint auf Ubuntu in Ordnung zu sein

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X