Eine Zeichenfolge in einen gültigen Dateinamen verwandeln?

298

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.

Sophie Gage
quelle
17
Sollte dies nicht in das Modul os.path integriert werden?
Endolith
2
Vielleicht, obwohl ihr Anwendungsfall einen einzigen Pfad erfordern würde, der auf allen Plattformen sicher ist , nicht nur den aktuellen, für den os.path nicht ausgelegt ist.
Javawizard
2
Um den obigen Kommentar zu erweitern: Das aktuelle Design von os.pathlädt je nach Betriebssystem tatsächlich eine andere Bibliothek (siehe den zweiten Hinweis in der Dokumentation ). Wenn also eine os.pathAnfü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.
Dshepherd

Antworten:

164

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.

def slugify(value):
    """
    Normalizes string, converts to lowercase, removes non-alpha characters,
    and converts spaces to hyphens.
    """
    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 weggelassen, da es nicht um Slugification geht, sondern um Flucht.

S.Lott
quelle
11
Die letzte Zeile sollte sein: Wert = Unicode (re ('[- \ s] +', '-', Wert))
Joseph Turian
1
Danke - Ich könnte etwas vermissen, aber ich bekomme: "normalize () Argument 2 muss Unicode sein, nicht str"
Alex Cook
"normalize () Argument 2". Bedeutet das 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.
S.Lott
8
Falls jemand die positive Seite dieses Ansatzes nicht bemerkt hat, ist, dass er nicht nur Nicht-Alpha-Zeichen entfernt, sondern zuerst versucht, gute Substitute zu finden (über die NFKD-Normalisierung), so dass é zu e wird, ein hochgestelltes 1 zu a wird Normal 1 usw. Danke
Michael Scott Cuthbert
48
Die slugifyFunktion wurde nach django / utils / text.py verschoben , und diese Datei enthält auch eine get_valid_filenameFunktion.
Denilson Sá Maia
104

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.

>>> 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'
Vinko Vrsalovic
quelle
7
valid_chars = frozenset(valid_chars)würde nicht schaden. Es ist 1,5-mal schneller, wenn es auf Allchars angewendet wird.
JFS
2
Warnung: Dadurch werden zwei verschiedene Zeichenfolgen derselben Zeichenfolge zugeordnet. >>> Zeichenfolge importieren >>> valid_chars = "- . ()% S% s"% (string.ascii_letters, string.digits) >>> valid_chars '- . () abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 '>>> filename = "a.com/hello/world" >>>' '.join (c für c in Dateiname wenn c in valid_chars)' a.comhelloworld '>>> ">>> '' .join (c für c im Dateinamen, wenn c in valid_chars) 'a.comhelloworld' >>>
Robert King
3
Ganz zu schweigen davon, dass das Benennen einer Datei "CON"unter Windows Sie in Schwierigkeiten bringen wird ...
Nathan Osman
2
Eine geringfügige Neuanordnung erleichtert die Angabe eines Ersatzzeichens. Zuerst die ursprüngliche Funktionalität: '' .join (c wenn c in valid_chars else '' für c in Dateiname) oder mit einem ersetzten Zeichen oder einer Zeichenfolge für jedes ungültige Zeichen: '' .join (c wenn c in valid_chars else '.' Für c in Dateiname)
PeterVermont
101

Sie können das Listenverständnis zusammen mit den Zeichenfolgenmethoden verwenden.

>>> s
'foo-bar#baz?qux@127/\\9]'
>>> "".join(x for x in s if x.isalnum())
'foobarbazqux1279'
John Mee
quelle
3
Beachten Sie, dass Sie die eckigen Klammern weglassen können. In diesem Fall wird ein Generatorausdruck an join übergeben, wodurch der Schritt zum Erstellen einer ansonsten nicht verwendeten Liste gespeichert wird.
Oben Sonne
31
+1 Liebte das. Eine geringfügige Änderung, die ich vorgenommen habe: "" .join ([x if x.isalnum () else "_" für x in s]) - würde ein Ergebnis ergeben, bei dem ungültige Elemente _ sind, als wären sie ausgeblendet. Vielleicht hilft das jemand anderem.
Eddie Parker
12
Diese Lösung ist großartig! Ich nahm jedoch eine geringfügige Änderung vor:filename = "".join(i for i in s if i not in "\/:*?<>|")
Alex Krycek
1
Leider erlaubt es nicht einmal Leerzeichen und Punkte, aber ich mag die Idee.
Tiktak
9
@tiktak: um (auch) Leerzeichen, Punkte und Unterstriche zuzulassen, die Sie "".join( x for x in s if (x.isalnum() or x in "._- "))
anstreben
95

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.

import base64
file_name_string = base64.urlsafe_b64encode(your_string)

Update : Geändert basierend auf Matthews Kommentar.

Igal Serban
quelle
1
Dies ist natürlich die beste Antwort, wenn dies der Fall ist.
user32141
60
Warnung! Die Base64-Codierung enthält standardmäßig das Zeichen "/" als gültige Ausgabe, die in Dateinamen auf vielen Systemen nicht gültig ist. Verwenden Sie stattdessen base64.urlsafe_b64encode (your_string)
Matthew
15
Tatsächlich ist die menschliche Lesbarkeit fast immer ein Faktor, auch wenn dies nur zu Debugging-Zwecken dient.
static_rtti
5
In Python 3 your_stringmuss ein Byte-Array sein oder das Ergebnis davon, encode('ascii')damit dies funktioniert.
Noumenon
4
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')
JeffProd
40

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.

Brian
quelle
24

Auf Github gibt es ein schönes Projekt namens python-slugify :

Installieren:

pip install python-slugify

Dann benutze:

>>> from slugify import slugify
>>> txt = "This\ is/ a%#$ test ---"
>>> slugify(txt)
'this-is-a-test'
Shoham
quelle
2
Ich mag diese Bibliothek, aber sie ist nicht so gut, wie ich dachte. Anfängliches Testen ok, aber es werden auch Punkte konvertiert. Also test.txtbekommt test-txtwas zu viel ist.
Therealmarv
23

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:

def get_valid_filename(s):
    s = str(s).strip().replace(' ', '_')
    return re.sub(r'(?u)[^-\w.]', '', s)

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

Cowlinator
quelle
4
für die Faulen schon auf Django:django.utils.text import get_valid_filename
Ansager
2
Wenn Sie mit Regex 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. 漢語.
Cowlinator
3
Vielleicht möchten Sie auch die Länge überprüfen: Dateinamen sind auf 255 Zeichen begrenzt (oder, wie Sie wissen, 32; abhängig von der FS)
Matthias Winkelmann
19

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 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.

Sophie Gage
quelle
Sie sollten in der Lage sein, uuid.uuid4 () für Ihr eindeutiges Präfix zu verwenden
slf
6
Kamel Fall .. ahh
Igel
Könnte dies bearbeitet / aktualisiert werden, um mit Python 3.6 zu funktionieren?
Wavesailor
13

Beachten Sie, dass es auf Unix-Systemen keine anderen Einschränkungen für Dateinamen gibt als

  • Es darf nicht \ 0 enthalten
  • Es darf / nicht enthalten

Alles andere ist Freiwild.

$ touch "
> sogar mehrzeilig
> haha
> ^ [[31m rot ^ [[0m
> böse "
$ ls -la 
-rw-r - r-- 0 17. November 23:39? sogar mehrzeilig? haha? [31m rot? [0m? böse
$ ls -lab
-rw-r - r-- 0 17. November 23:39 \ neven \ multiline \ nhaha \ n \ 033 [31m \ red \ \ 033 [0m \ nevil
$ perl -e 'für mein $ i (glob (q {./* gerade *})) {print $ i; } '
./
sogar mehrzeilig
Haha
 rot 
böse

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;)

Kent Fredric
quelle
Das OP gibt an, dass "der Dateiname auf mehreren Betriebssystemen gültig sein muss"
Cowlinator
1
@owlinator, dass 10 Stunden nach dem Absenden meiner Antwort eine Klarstellung hinzugefügt wurde :) Überprüfen Sie das Bearbeitungsprotokoll des OP.
Kent Fredric
12

In einer Zeile:

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

Sie können auch das Zeichen '_' eingeben, um die Lesbarkeit zu verbessern (z. B. beim Ersetzen von Schrägstrichen).

mnach
quelle
7

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.

import re

str = "File!name?.txt"
f = open(os.path.join("/tmp", re.sub('[^-a-zA-Z0-9_.() ]+', '', str))

Würde zu einem Dateihandle zu /tmp/filename.txt führen.

gx.
quelle
5
Sie müssen den Bindestrich zuerst im Gruppen-Matcher verwenden, damit er nicht als Bereich angezeigt wird. re ('[^ - a-zA-Z0-9 _. ()] +', '', str)
Phord
7
>>> import string
>>> safechars = bytearray(('_-.()' + string.digits + string.ascii_letters).encode())
>>> allchars = bytearray(range(0x100))
>>> deletechars = bytearray(set(allchars) - set(safechars))
>>> filename = u'#ab\xa0c.$%.txt'
>>> safe_filename = filename.encode('ascii', 'ignore').translate(None, deletechars).decode()
>>> safe_filename
'abc..txt'

Es werden keine leeren Zeichenfolgen, speziellen Dateinamen ('nul', 'con' usw.) verarbeitet.

jfs
quelle
+1 für Übersetzungstabellen ist dies bei weitem die effizienteste Methode. Für die speziellen Dateinamen / Leergut reicht eine einfache Vorbedingungsprüfung aus, und für Nebenzeiten ist dies ebenfalls eine einfache Korrektur.
Christian Witts
1
Während die Übersetzung etwas effizienter ist als ein regulärer Ausdruck, wird diese Zeit höchstwahrscheinlich in den Schatten gestellt, wenn Sie tatsächlich versuchen, die Datei zu öffnen, was Sie zweifellos beabsichtigen. Daher bevorzuge ich eher eine besser lesbare Regexp-Lösung als das oben beschriebene Chaos
nosatalian
Ich mache mir auch Sorgen um die schwarze Liste. Zugegeben, es ist eine schwarze Liste, die auf einer Whitelist basiert, aber immer noch. Es scheint weniger ... sicher. Woher weißt du, dass "allchars" tatsächlich vollständig ist?
isaaclw
@isaaclw: '.translate ()' akzeptiert eine Zeichenfolge mit 256 Zeichen als Übersetzungstabelle (Byte-zu-Byte-Übersetzung). '.maketrans ()' erstellt eine solche Zeichenfolge. Alle Werte sind abgedeckt; Es ist ein reiner Whitelist-Ansatz
JFS
Was ist mit dem Dateinamen '.' (ein einzelner Punkt). Dies würde unter Unixen nicht funktionieren, da das aktuelle Verzeichnis diesen Namen verwendet.
Finn Årup Nielsen
6

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.

Karlcow
quelle
6

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.

James Anderson
quelle
5
Ist der Name trotzdem gültig? Ich meine, wenn das Betriebssystem nicht zufrieden ist, müssen Sie trotzdem etwas tun, oder?
Jeromej
1
In einigen Fällen kann das Betriebssystem / die Sprache Ihren Dateinamen stillschweigend in eine alternative Form umwandeln. Wenn Sie jedoch eine Verzeichnisliste erstellen, wird ein anderer Name angezeigt. Und dies kann zu einem Problem führen, dass "wenn ich die Datei dort schreibe, aber wenn ich nach der Datei suche, wird sie als etwas anderes bezeichnet". (Ich spreche über Verhalten, von dem ich auf VAX gehört habe ...)
Kent Fredric
Darüber hinaus muss "Der Dateiname muss auf mehreren Betriebssystemen gültig sein", was Sie bei einer osopenAusführung auf einem Computer nicht erkennen können .
LarsH
5

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:

import re
badchars= re.compile(r'[^A-Za-z0-9_. ]+|^\.|\.$|^ | $|^$')
badnames= re.compile(r'(aux|com[1-9]|con|lpt[1-9]|prn)(\.|$)')

def makeName(s):
    name= badchars.sub('_', s)
    if badnames.match(name):
        name= '_'+name
    return name

Selbst dies kann nicht richtig garantiert werden, insbesondere auf unerwarteten Betriebssystemen - zum Beispiel hasst RISC OS Leerzeichen und verwendet '.' als Verzeichnistrennzeichen.

Bobince
quelle
4

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:

pip install python-slugify

Beispielcode:

s = 'Very / Unsafe / file\nname hähä \n\r .txt'
clean_basename = slugify(os.path.splitext(s)[0])
clean_extension = slugify(os.path.splitext(s)[1][1:])
if clean_extension:
    clean_filename = '{}.{}'.format(clean_basename, clean_extension)
elif clean_basename:
    clean_filename = clean_basename
else:
    clean_filename = 'none' # only unclean characters

Ausgabe:

>>> clean_filename
'very-unsafe-file-name-haha.txt'

Dies ist so ausfallsicher, dass es mit Dateinamen ohne Erweiterung funktioniert und sogar nur mit Dateinamen für unsichere Zeichen (Ergebnis nonehier).

therealmarv
quelle
1
Ich mag das, erfinde das Rad nicht neu, importiere nicht das gesamte Django-Framework, wenn du es nicht brauchst, füge den Code nicht direkt ein, wenn du ihn in Zukunft nicht mehr pflegen willst, und generiere String-Versuche um ähnliche Buchstaben mit sicheren Buchstaben abzugleichen, ist eine neue Zeichenfolge leichter zu lesen.
Vicenteherrera
1
So verwenden Sie Unterstrich anstelle von Bindestrich: name = slugify (s, separator = '_')
vicenteherrera
3

Antwort geändert für Python 3.6

import string
import unicodedata

validFilenameChars = "-_.() %s%s" % (string.ascii_letters, string.digits)
def removeDisallowedFilenameChars(filename):
    cleanedFilename = unicodedata.normalize('NFKD', filename).encode('ASCII', 'ignore')
    return ''.join(chr(c) for c in cleanedFilename if chr(c) in validFilenameChars)
Jean-Robin Tremblay
quelle
Können Sie Ihre Antwort ausführlich erläutern?
Gelassenheit
Es ist die gleiche Antwort, die Sophie Gage akzeptiert hat. Aber es wurde geändert, um auf Python 3.6 zu arbeiten
Jean-Robin Tremblay
2

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.

def normalizefilename(fn):
    validchars = "-_.() "
    out = ""
    for c in fn:
      if str.isalpha(c) or str.isdigit(c) or (c in validchars):
        out += c
      else:
        out += "_"
    return out    

Wenn Sie möchten, können Sie der validcharsVariablen 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 ifTrennzeichen für den OS-Pfad zu ändern .

Tuncay Göncüoğlu
quelle
1

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:

{'helloworld': 
    (
    {'/hello/world': 'helloworld', '/helloworld/': 'helloworld1'},
    2)
    }

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.

Robert King
quelle
Beachten Sie, wenn Sie helloworld1 verwenden, müssen Sie auch überprüfen, ob helloworld1 nicht verwendet wird und so weiter.
Robert King
1

Nicht genau das, wonach OP gefragt hat, aber das verwende ich, weil ich eindeutige und reversible Konvertierungen benötige:

# p3 code
def safePath (url):
    return ''.join(map(lambda ch: chr(ch) if ch in safePath.chars else '%%%02x' % ch, url.encode('utf-8')))
safePath.chars = set(map(lambda x: ord(x), '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-_ .'))

Das Ergebnis ist "etwas" lesbar, zumindest aus Sicht des Systemadministrators.

Makeroo
quelle
Ein Wrapper dafür ohne Leerzeichen in def safe_filename(filename): return safePath(filename.strip().replace(' ','_'))
Dateinamen
1

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 :

from pathvalidate import sanitize_filename

fname = "fi:l*e/p\"a?t>h|.t<xt"
print(f"{fname} -> {sanitize_filename(fname)}\n")
fname = "\0_a*b:c<d>e%f/(g)h+i_0.txt"
print(f"{fname} -> {sanitize_filename(fname)}\n")

Ausgabe

fi:l*e/p"a?t>h|.t<xt -> filepath.txt
_a*b:c<d>e%f/(g)h+i_0.txt -> _abcde%f(g)h+i_0.txt
Stavros
quelle
0

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:

import string
for chr in your_string:
 if chr == ' ':
   your_string = your_string.replace(' ', '_')
 elif chr not in string.ascii_letters or chr not in string.digits:
    your_string = your_string.replace(chr, '')
TankorSmash
quelle
Ich habe dies "".join( x for x in s if (x.isalnum() or x in "._- "))in diesem Beitrag Kommentare gefunden
SergioAraujo
0

AKTUALISIEREN

Alle Links sind in dieser 6 Jahre alten Antwort irreparabel gebrochen.

Außerdem würde ich es auch nicht mehr so ​​machen, nur base64unsichere Zeichen verschlüsseln oder löschen. Beispiel für Python 3:

import re
t = re.compile("[a-zA-Z0-9.,_-]")
unsafe = "abc∂éåß®∆˚˙©¬ñ√ƒµ©∆∫ø"
safe = [ch for ch in unsafe if t.match(ch)]
# => 'abc'

Mit können base64Sie 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.

from random import choice
from string import ascii_lowercase, ascii_uppercase, digits
allowed_chr = ascii_lowercase + ascii_uppercase + digits

safe = ''.join([choice(allowed_chr) for _ in range(16)])
# => 'CYQ4JDKE9JfcRzAZ'

ORIGINAL LINKROTTEN ANTWORT :

Das bobcatProjekt enthält ein Python-Modul, das genau dies tut.

Es ist nicht ganz robust, siehe diesen Beitrag und diese Antwort .

Wie bereits erwähnt: base64Codierung ist wahrscheinlich eine bessere Idee, wenn die Lesbarkeit keine Rolle spielt.

Drähte
quelle
Alle Links tot. Mann, mach etwas.
Der friedliche Kodierer