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.

300voto

S.Lott Punkte 371691

Sie können sich das Django Framework ansehen (aber beachten Sie deren Lizenz!), wie sie aus beliebigem Text einen "Slug" erstellen. Ein Slug ist URL- und Dateinamen-freundlich.

Die Django-Text-Utilities definieren eine Funktion, slugify(), die wahrscheinlich der Goldstandard für diese Art von Dingen ist. Im Wesentlichen sieht ihr Code wie folgt aus.

import unicodedata
import re

def slugify(value, allow_unicode=False):
    """
    Übernommen von https://github.com/django/django/blob/master/django/utils/text.py
    Konvertiert in ASCII, wenn 'allow_unicode' False ist. Konvertiert Leerzeichen oder wiederholte Bindestriche in einzelne Bindestriche. Entfernt Zeichen, die keine alphanumerischen Zeichen, Unterstriche oder Bindestriche sind. Konvertiert in Kleinbuchstaben. Entfernt auch führende und abschließende Leerzeichen, Bindestriche und Unterstriche.
    """
    value = str(value)
    if allow_unicode:
        value = unicodedata.normalize('NFKC', value)
    else:
        value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
    value = re.sub(r'[^\w\s-]', '', value.lower())
    return re.sub(r'[-\s]+', '-', value).strip('-_')

Und die ältere Version:

def slugify(value):
    """
    Normalisiert den String, konvertiert in Kleinbuchstaben, entfernt nicht-alfa-Zeichen und konvertiert Leerzeichen in Bindestriche.
    """
    import unicodedata
    value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
    value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
    value = unicode(re.sub('[-\s]+', '-', value))
    # ...
    return value

Es gibt noch mehr, aber ich habe es ausgelassen, da es nicht die Slug-Erstellung behandelt, sondern das Escapen.

13 Stimmen

Der letzte Satz sollte sein: Wert = unicode(re.sub('[-\s]+', ' - ', Wert))

1 Stimmen

Danke - Ich könnte etwas übersehen, aber ich bekomme: "normalize() Argument 2 muss Unicode sein, nicht str"

0 Stimmen

"normalize() Argument 2". Bedeutet den Wert. Wenn der Wert Unicode sein muss, dann müssen Sie sicherstellen, dass es tatsächlich Unicode ist. Oder. Sie möchten die Normalisierung von Unicode möglicherweise auslassen, wenn Ihr tatsächlicher Wert tatsächlich eine ASCII-Zeichenkette ist.

192voto

Sie können list comprehension zusammen mit den String-Methoden verwenden.

>>> s
'foo-bar#baz?qux@127/\\9]'
>>> "".join(x for x in s if x.isalnum())
'foobarbazqux1279'

0 Stimmen

Obwohl es nicht die wenigen zusätzlichen Zeichen enthält, die er wollte "_-.() ". Trotzdem meine bevorzugte Lösung ;)

5 Stimmen

Beachten Sie, dass Sie die eckigen Klammern auslassen können. In diesem Fall wird ein Generatorausdruck an join übergeben, wodurch der Schritt des Erstellens einer ansonsten ungenutzten Liste eingespart wird.

63 Stimmen

+1 Das habe ich geliebt. Geringfügige Änderung, die ich vorgenommen habe: "".join([x if x.isalnum() else "_" for x in s]) -- würde ein Ergebnis liefern, bei dem ungültige Elemente _, also wie leer, sind. Vielleicht hilft das jemand anderem.

118voto

Igal Serban Punkte 10330

Was ist der Grund, Zeichenfolgen als Dateinamen zu verwenden? Wenn die menschliche Lesbarkeit keine Rolle spielt, würde ich das base64-Modul verwenden, das Dateisystem-sichere Zeichenfolgen erzeugen kann. Es wird zwar nicht lesbar sein, aber Sie werden keine Probleme mit Kollisionen haben und es ist umkehrbar.

import base64
dateiname_string = base64.urlsafe_b64encode(your_string)

Aktualisierung: Geändert basierend auf dem Kommentar von Matthew.

83 Stimmen

Achtung! Die base64-Codierung enthält standardmäßig das "/"-Zeichen als gültige Ausgabe, was auf vielen Systemen in Dateinamen ungültig ist. Verwenden Sie stattdessen base64.urlsafe_b64encode(your_string)

3 Stimmen

Dies sollte absolut als die ideale Antwort für Webserver mit beliebigem internen benutzerdefinierten Inhalt angesehen werden. Selbst wenn der Administrator etwas suchen muss, können Sie problemlos ein Skript schreiben, um alle Anfragen in die gleiche Form umzuwandeln.

27 Stimmen

Eigentlich ist die Lesbarkeit für Menschen fast immer ein Faktor, auch wenn nur für Debugging-Zwecke.

114voto

Vinko Vrsalovic Punkte 252104

Dieser Whitelist-Ansatz (d.h. nur die in valid_chars vorhandenen Zeichen zuzulassen) funktioniert, wenn es keine Einschränkungen für die Formatierung der Dateien oder Kombinationen von gültigen Zeichen gibt, die verboten sind (wie z. B. ".."), zum Beispiel würde dein Ansatz es erlauben, eine Datei mit dem Namen ".txt" zu erstellen, was meiner Meinung nach auf Windows nicht gültig ist. Da dies der einfachste Ansatz ist, würde ich versuchen, Leerzeichen aus den gültigen Zeichen zu entfernen und im Fehlerfall einen bekannten gültigen String voranzustellen, jeder andere Ansatz müsste wissen, was wo erlaubt ist, um mit den Einschränkungen für Windows-Dateinamen umgehen zu können und dadurch wesentlich komplexer sein.

>>> import string
>>> valid_chars = "-_.() %s%s" % (string.ascii_letters, string.digits)
>>> valid_chars
'-_.() abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
>>> filename = "This Is a (valid) - filename%$&$ .txt"
>>> ''.join(c for c in filename if c in valid_chars)
'This Is a (valid) - filename .txt'

9 Stimmen

valid_chars = frozenset(valid_chars) würde nicht schaden. Es ist 1,5-mal schneller, wenn es auf allchars angewendet wird.

2 Stimmen

Warnung: Dieses ordnet zwei verschiedenen Zeichenfolgen dieselbe Zeichenfolge zu >>> importieren string >>> gültige_chars = "-.() %s%s" % (string.ascii_letters, string.digits) >>> gültige_chars '-.() abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567‌​89' >>> Dateiname = "a.com/hello/world" >>> ''.join(c für c in Dateinamefalls c in gültige_chars) 'a.comhelloworld' >>> Dateiname = "a.com/helloworld" >>> ''.join(c für c in Dateinamefalls c in gültige_chars) 'a.comhelloworld' >>>

4 Stimmen

Nicht zu vergessen, dass die Benennung einer Datei "CON" unter Windows zu Problemen führen wird...

57voto

Shoham Punkte 6216

Es gibt ein schönes Projekt auf Github namens python-slugify:

Installieren:

pip install python-slugify

Dann verwenden:

>>> from slugify import slugify
>>> txt = "This\ is/ a%#$ test ---"
>>> slugify(txt)
'this-is-a-test'

3 Stimmen

Ich mag diese Bibliothek, aber sie ist nicht so gut, wie ich dachte. Die anfängliche Prüfung ist in Ordnung, aber sie konvertiert auch Punkte. Also wird test.txt zu test-txt, was zu viel ist.

1 Stimmen

Slugify scheint aktiv gepflegt zu werden, und die aktuelle (Stand 2021-11-03) Version bietet viele Optionen, von denen einige verwendet werden könnten, um die . und - Substitution zu steuern. Siehe auch die Kommentare zu @therealmarv's Antwort unten.

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