Ich erhalte eine Zeichenfolge von einem externen Prozess. Ich möchte diesen String verwenden, um einen Dateinamen zu erstellen und dann in diese Datei zu schreiben. Hier ist mein Code-Snippet, um dies zu tun:
String s = ... // comes from external source
File currentFile = new File(System.getProperty("user.home"), s);
PrintWriter currentWriter = new PrintWriter(currentFile);
Wenn s ein ungültiges Zeichen enthält, z. B. '/' in einem Unix-basierten Betriebssystem, wird (zu Recht) eine java.io.FileNotFoundException ausgelöst.
Wie kann ich den String sicher codieren, damit er als Dateiname verwendet werden kann?
Bearbeiten: Was ich mir erhoffe, ist ein API-Aufruf, der dies für mich erledigt.
Ich kann dies tun:
String s = ... // comes from external source
File currentFile = new File(System.getProperty("user.home"), URLEncoder.encode(s, "UTF-8"));
PrintWriter currentWriter = new PrintWriter(currentFile);
Ich bin mir aber nicht sicher, ob URLEncoder für diesen Zweck zuverlässig ist.
Antworten:
Wenn Sie möchten, dass das Ergebnis der Originaldatei ähnelt, ist SHA-1 oder ein anderes Hashing-Schema nicht die Antwort. Wenn Kollisionen vermieden werden müssen, ist das einfache Ersetzen oder Entfernen von "schlechten" Zeichen ebenfalls nicht die Antwort.
Stattdessen willst du so etwas. (Hinweis: Dies sollte als veranschaulichendes Beispiel betrachtet werden und nicht zum Kopieren und Einfügen.)
Diese Lösung bietet eine reversible Codierung (ohne Kollisionen), bei der die codierten Zeichenfolgen in den meisten Fällen den ursprünglichen Zeichenfolgen ähneln. Ich gehe davon aus, dass Sie 8-Bit-Zeichen verwenden.
URLEncoder
funktioniert, hat aber den Nachteil, dass es eine ganze Reihe von legalen Dateinamenzeichen codiert.Wenn Sie eine nicht garantierte, reversible Lösung wünschen, entfernen Sie einfach die "schlechten" Zeichen, anstatt sie durch Escape-Sequenzen zu ersetzen.
Die Umkehrung der obigen Codierung sollte ebenso einfach zu implementieren sein.
quelle
Mein Vorschlag ist, eine "weiße Liste" zu wählen, dh nicht zu versuchen, schlechte Zeichen herauszufiltern. Definieren Sie stattdessen, was in Ordnung ist. Sie können den Dateinamen entweder ablehnen oder filtern. Wenn Sie es filtern möchten:
Dies ersetzt jedes Zeichen, das keine Zahl, kein Buchstabe oder Unterstrich ist, durch nichts. Alternativ können Sie sie durch ein anderes Zeichen ersetzen (z. B. einen Unterstrich).
Das Problem ist, dass Sie, wenn dies ein freigegebenes Verzeichnis ist, keine Kollision mit Dateinamen möchten. Selbst wenn die Speicherbereiche der Benutzer nach Benutzern getrennt sind, kann dies zu einem kollidierenden Dateinamen führen, indem nur fehlerhafte Zeichen herausgefiltert werden. Der Name, den ein Benutzer eingibt, ist oft nützlich, wenn er ihn jemals herunterladen möchte.
Aus diesem Grund erlaube ich dem Benutzer, das einzugeben, was er möchte, den Dateinamen basierend auf einem Schema meiner Wahl (z. B. userId_fileId) zu speichern und dann den Dateinamen des Benutzers in einer Datenbanktabelle zu speichern. Auf diese Weise können Sie es dem Benutzer wieder anzeigen, die gewünschten Informationen speichern und die Sicherheit nicht beeinträchtigen oder andere Dateien löschen.
Sie können die Datei auch hashen (z. B. MD5-Hash), aber dann können Sie die vom Benutzer eingegebenen Dateien nicht auflisten (ohnehin nicht mit einem aussagekräftigen Namen).
EDIT: Regex für Java behoben
quelle
"\\W+"
für den regulären Ausdruck in Java verwenden. Backslash gilt zuerst für die Zeichenfolge selbst und\W
ist keine gültige Escape-Sequenz. Ich habe versucht, die Antwort zu bearbeiten, aber es sieht so aus, als hätte jemand meine Bearbeitung abgelehnt :(Dies hängt davon ab, ob die Codierung reversibel sein soll oder nicht.
Reversibel
Verwenden Sie die URL-Codierung (
java.net.URLEncoder
), um Sonderzeichen durch zu ersetzen%xx
. Beachten Sie, dass Sie sich um die Sonderfälle kümmern, in denen die Zeichenfolge gleich.
, gleich..
oder leer ist! ¹ Viele Programme verwenden URL-Codierung, um Dateinamen zu erstellen. Dies ist also eine Standardtechnik, die jeder versteht.Irreversibel
Verwenden Sie einen Hash (z. B. SHA-1) der angegebenen Zeichenfolge. Moderne Hash-Algorithmen ( nicht MD5) können als kollisionsfrei angesehen werden. In der Tat haben Sie einen Durchbruch in der Kryptographie, wenn Sie eine Kollision finden.
¹ Sie können alle 3 Sonderfälle elegant behandeln, indem Sie ein Präfix wie z
"myApp-"
. Wenn Sie die Datei direkt in$HOME
ablegen, müssen Sie dies trotzdem tun, um Konflikte mit vorhandenen Dateien wie ".bashrc" zu vermeiden.quelle
Folgendes verwende ich:
Dadurch wird jedes Zeichen, das kein Buchstabe, keine Zahl, kein Unterstrich oder kein Punkt ist, durch einen Unterstrich durch Regex ersetzt.
Dies bedeutet, dass etwas wie "Wie man £ in $ umwandelt" zu "How_to_convert___to__" wird. Zugegeben, dieses Ergebnis ist nicht sehr benutzerfreundlich, aber es ist sicher und die resultierenden Verzeichnis- / Dateinamen funktionieren garantiert überall. In meinem Fall wird das Ergebnis dem Benutzer nicht angezeigt und ist daher kein Problem. Möglicherweise möchten Sie jedoch den regulären Ausdruck so ändern, dass er zulässiger ist.
Es ist erwähnenswert, dass ein weiteres Problem darin bestand, dass ich manchmal identische Namen erhielt (da dies auf Benutzereingaben basiert). Sie sollten sich dessen bewusst sein, da Sie nicht mehrere Verzeichnisse / Dateien mit demselben Namen in einem einzigen Verzeichnis haben können . Ich habe nur die aktuelle Uhrzeit und das aktuelle Datum sowie eine kurze zufällige Zeichenfolge vorangestellt, um dies zu vermeiden. (Eine tatsächliche zufällige Zeichenfolge, kein Hash des Dateinamens, da identische Dateinamen zu identischen Hashes führen.)
Außerdem müssen Sie möglicherweise die resultierende Zeichenfolge abschneiden oder auf andere Weise kürzen, da sie möglicherweise die 255-Zeichen-Grenze überschreitet, die einige Systeme haben.
quelle
Für diejenigen, die nach einer allgemeinen Lösung suchen, können dies häufige Kriterien sein:
Um dies zu erreichen, können wir Regex verwenden, um unzulässige Zeichen abzugleichen, sie in Prozent zu codieren und dann die Länge der codierten Zeichenfolge zu beschränken.
Muster
Das obige Muster basiert auf einer konservativen Teilmenge zulässiger Zeichen in der POSIX-Spezifikation .
Wenn Sie das Punktzeichen zulassen möchten, verwenden Sie:
Seien Sie vorsichtig mit Saiten wie "." und ".."
Wenn Sie Kollisionen in Dateisystemen ohne Berücksichtigung der Groß- und Kleinschreibung vermeiden möchten, müssen Sie Großbuchstaben umgehen:
Oder entkommen Sie Kleinbuchstaben:
Anstatt eine Whitelist zu verwenden, können Sie reservierte Zeichen für Ihr spezifisches Dateisystem auf die schwarze Liste setzen. EG Dieser reguläre Ausdruck passt zu FAT32-Dateisystemen:
Länge
Unter Android sind 127 Zeichen die sichere Grenze. Viele Dateisysteme erlauben 255 Zeichen.
Wenn Sie lieber den Schwanz als den Kopf Ihrer Schnur behalten möchten, verwenden Sie:
Dekodierung
Verwenden Sie Folgendes, um den Dateinamen wieder in die ursprüngliche Zeichenfolge zu konvertieren:
Einschränkungen
Da längere Zeichenfolgen abgeschnitten werden, besteht die Möglichkeit einer Namenskollision beim Codieren oder einer Beschädigung beim Decodieren.
quelle
Pattern.compile("[^A-Za-z0-9_\\-]")
Verwenden Sie den folgenden regulären Ausdruck, der jedes ungültige Dateinamenzeichen durch ein Leerzeichen ersetzt:
quelle
_
oder zu ersetzen-
.Wählen Sie Ihr Gift aus den Optionen des Commons-Codecs aus . Beispiel:
quelle
sha1
;sha
ist veraltet.Dies ist wahrscheinlich nicht der effektivste Weg, zeigt jedoch, wie dies mit Java 8-Pipelines gemacht wird:
Die Lösung könnte verbessert werden, indem ein benutzerdefinierter Kollektor erstellt wird, der StringBuilder verwendet, sodass Sie nicht jedes leichte Zeichen in eine schwere Zeichenfolge umwandeln müssen.
quelle
Sie können die ungültigen Zeichen ('/', '\', '?', '*') Entfernen und dann verwenden.
quelle