Wie entferne ich unzulässige Zeichen aus Pfad und Dateinamen?

456

Ich brauche eine robuste und einfache Methode, um unzulässige Pfad- und Dateizeichen aus einer einfachen Zeichenfolge zu entfernen. Ich habe den folgenden Code verwendet, aber er scheint nichts zu bewirken. Was fehlt mir?

using System;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string illegal = "\"M<>\"\\a/ry/ h**ad:>> a\\/:*?\"<>| li*tt|le|| la\"mb.?";

            illegal = illegal.Trim(Path.GetInvalidFileNameChars());
            illegal = illegal.Trim(Path.GetInvalidPathChars());

            Console.WriteLine(illegal);
            Console.ReadLine();
        }
    }
}
Gary Willoughby
quelle
1
Beim Trimmen werden Zeichen vom Anfang und Ende der Zeichenfolgen entfernt. Sie sollten sich jedoch wahrscheinlich fragen, warum die Daten ungültig sind, und die Daten ablehnen, anstatt zu versuchen, sie zu bereinigen / zu reparieren.
user7116
8
Unix-Stilnamen sind unter Windows nicht gültig und ich möchte mich nicht mit 8.3-Kurznamen befassen.
Gary Willoughby
GetInvalidFileNameChars()entfernt Dinge wie: \ etc aus Ordnerpfaden.
CAD Kerl
1
Path.GetInvalidPathChars()scheint nicht zu streifen *oder?
CAD Kerl
18
Ich habe fünf Antworten auf diese Frage getestet (Zeitschleife von 100.000) und die folgende Methode ist die schnellste. Der reguläre Ausdruck belegte den 2. Platz und war 25% langsamer: public string GetSafeFilename (string filename) {return string.Join ("_", filename.Split (Path.GetInvalidFileNameChars ())); }
Brain2000

Antworten:

494

Versuchen Sie stattdessen so etwas;

string illegal = "\"M\"\\a/ry/ h**ad:>> a\\/:*?\"| li*tt|le|| la\"mb.?";
string invalid = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());

foreach (char c in invalid)
{
    illegal = illegal.Replace(c.ToString(), ""); 
}

Aber ich muss den Kommentaren zustimmen, ich würde wahrscheinlich versuchen, mit der Quelle der illegalen Pfade umzugehen, anstatt zu versuchen, einen illegalen Pfad in einen legitimen, aber wahrscheinlich unbeabsichtigten Pfad zu verwandeln.

Bearbeiten: Oder eine potenziell "bessere" Lösung mit Regex.

string illegal = "\"M\"\\a/ry/ h**ad:>> a\\/:*?\"| li*tt|le|| la\"mb.?";
string regexSearch = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
Regex r = new Regex(string.Format("[{0}]", Regex.Escape(regexSearch)));
illegal = r.Replace(illegal, "");

Dennoch muss die Frage gestellt werden, warum Sie dies überhaupt tun.

Matthew Scharley
quelle
40
Es ist nicht erforderlich, die beiden Listen zusammenzufügen. Die Zeichenliste für unzulässige Dateinamen enthält die Zeichenliste für unzulässige Pfade und einige weitere. Hier sind Listen beider Listen, die in int umgewandelt wurden: 34,60,62,124,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,58,42,63,92,47 34,60,62,124,0,1,2 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27 , 28,29,30,31
Sarel Botha
9
@sjbotha Dies kann unter Windows und der Implementierung von .NET durch Microsoft zutreffen. Ich bin nicht bereit, die gleiche Annahme für beispielsweise Mono unter Linux zu treffen.
Matthew Scharley
7
In Bezug auf die erste Lösung. Sollte ein StringBuilder nicht effizienter sein als die Stringzuweisungen?
Epignosisx
6
@MatthewScharley, die Mono-Implementierung von GetInvalidPathChars () gibt nur 0x00 zurück und GetInvalidFileNameChars () gibt nur 0x00 und '/' zurück, wenn sie auf Nicht-Windows-Plattformen ausgeführt werden. Unter Windows sind die Listen ungültiger Zeichen viel länger, und GetInvalidPathChars () wird vollständig in GetInvalidFileNameChars () dupliziert. Dies wird sich in absehbarer Zeit nicht ändern. Sie verdoppeln also nur die Zeit, die diese Funktion benötigt, um ausgeführt zu werden, da Sie befürchten, dass sich die Definition eines gültigen Pfads bald ändern wird. Was es nicht wird.
Warren Rumak
13
@Charleh diese Diskussion ist so unnötig ... Code sollte immer optimiert werden und es besteht kein Risiko, dass dies falsch ist. Ein Dateiname ist ebenfalls Teil des Pfades. Es ist also nur unlogisch, GetInvalidPathChars()Zeichen zu enthalten, die GetInvalidFileNameChars()dies nicht tun würden. Sie übernehmen keine Korrektheit gegenüber "vorzeitiger" Optimierung. Sie verwenden einfach schlechten Code.
Stefan Fabian
353

Die ursprüngliche Frage lautete "Unzulässige Zeichen entfernen":

public string RemoveInvalidChars(string filename)
{
    return string.Concat(filename.Split(Path.GetInvalidFileNameChars()));
}

Möglicherweise möchten Sie sie stattdessen ersetzen:

public string ReplaceInvalidChars(string filename)
{
    return string.Join("_", filename.Split(Path.GetInvalidFileNameChars()));    
}

Diese Antwort war in einem anderen Thread von Ceres , ich mag es wirklich ordentlich und einfach.

Shehab Fawzy
quelle
10
Um die Frage des OP genau zu beantworten, müssten Sie "" anstelle von "_" verwenden, aber Ihre Antwort trifft in der Praxis wahrscheinlich auf mehr von uns zu. Ich denke, dass das Ersetzen illegaler Zeichen durch legale Zeichen häufiger vorkommt.
BH
36
Ich habe fünf Methoden aus dieser Frage getestet (Zeitschleife von 100.000) und diese Methode ist die schnellste. Der reguläre Ausdruck belegte den 2. Platz und war 25% langsamer als diese Methode.
Brain2000
10
Um den Kommentar von @BH zu adressieren, kann man einfach string.Concat (name.Split (Path.GetInvalidFileNameChars ()))
Michael Sutton
210

Ich benutze Linq, um Dateinamen zu bereinigen. Sie können dies problemlos erweitern, um auch nach gültigen Pfaden zu suchen.

private static string CleanFileName(string fileName)
{
    return Path.GetInvalidFileNameChars().Aggregate(fileName, (current, c) => current.Replace(c.ToString(), string.Empty));
}

Aktualisieren

Einige Kommentare weisen darauf hin, dass diese Methode für sie nicht funktioniert. Daher habe ich einen Link zu einem DotNetFiddle-Snippet eingefügt, damit Sie die Methode validieren können.

https://dotnetfiddle.net/nw1SWY

Michael Minton
quelle
4
Das hat bei mir nicht funktioniert. Die Methode gibt die saubere Zeichenfolge nicht zurück. Es gibt den übergebenen Dateinamen so zurück, wie er ist.
Karan
Was @Karan gesagt hat, funktioniert nicht, die ursprüngliche Zeichenfolge kommt zurück.
Jon
Mit Linq können Sie dies jedoch folgendermaßen tun : var invalid = new HashSet<char>(Path.GetInvalidPathChars()); return new string(originalString.Where(s => !invalid.Contains(s)).ToArray()). Die Leistung ist wahrscheinlich nicht großartig, aber das spielt wahrscheinlich keine Rolle.
Casey
2
@Karan oder Jon Welche Eingabe senden Sie diese Funktion? Siehe meine Bearbeitung zur Überprüfung dieser Methode.
Michael Minton
3
Es ist einfach - Jungs haben Strings mit gültigen Zeichen übergeben. Upvoted für coole Aggregatlösung.
Nickmaovich
89

Sie können illegale Zeichen mit Linq folgendermaßen entfernen:

var invalidChars = Path.GetInvalidFileNameChars();

var invalidCharsRemoved = stringWithInvalidChars
.Where(x => !invalidChars.Contains(x))
.ToArray();

BEARBEITEN
So sieht es mit der erforderlichen Bearbeitung aus, die in den Kommentaren erwähnt wird:

var invalidChars = Path.GetInvalidFileNameChars();

string invalidCharsRemoved = new string(stringWithInvalidChars
  .Where(x => !invalidChars.Contains(x))
  .ToArray());
Gregor Slavec
quelle
1
Ich mag diesen Weg: Sie behalten nur die erlaubten Zeichen in der Zeichenfolge (die nichts anderes als ein Zeichenarray ist).
Dude Pascalou
6
Ich weiß, dass dies eine alte Frage ist, aber dies ist eine großartige Antwort. Ich wollte jedoch hinzufügen, dass Sie in c # weder implizit noch explizit (verrückt, ich weiß) von char [] in string umwandeln können, sodass Sie es in einen String-Konstruktor ablegen müssen.
JNYRanger
1
Ich habe dies nicht bestätigt, aber ich erwarte, dass Path.GetInvalidPathChars () eine Obermenge von GetInvalidFileNameChars () ist und sowohl Dateinamen als auch Pfade abdeckt. Daher würde ich das wahrscheinlich stattdessen verwenden.
Angularsen
3
@anjdreas tatsächlich Path.GetInvalidPathChars () scheint eine Teilmenge von Path.GetInvalidFileNameChars () zu sein, nicht umgekehrt. Path.GetInvalidPathChars () gibt beispielsweise nicht '?' Zurück.
Rafael Costa
1
Das ist eine gute Antwort. Ich verwende sowohl die Dateinamenliste als auch die Dateipfadliste: ____________________________ Zeichenfolge cleanData = neue Zeichenfolge (data.Where (x =>! Path.GetInvalidFileNameChars (). Enthält (x) &&! Path.GetInvalidPathChars (). Enthält (x)). ToArray ());
Goamn
27

Dies sind alles großartige Lösungen, auf die sich jedoch alle verlassen Path.GetInvalidFileNameChars, die möglicherweise nicht so zuverlässig sind, wie Sie denken. Beachten Sie die folgende Bemerkung in der MSDN-Dokumentation zu Path.GetInvalidFileNameChars:

Es wird nicht garantiert, dass das von dieser Methode zurückgegebene Array den vollständigen Satz von Zeichen enthält, die in Datei- und Verzeichnisnamen ungültig sind. Der vollständige Satz ungültiger Zeichen kann je nach Dateisystem variieren. Auf Windows-basierten Desktop-Plattformen können ungültige Pfadzeichen beispielsweise ASCII / Unicode-Zeichen 1 bis 31 sowie Anführungszeichen ("), kleiner als (<), größer als (>), Pipe (|), Rücktaste () enthalten. \ b), null (\ 0) und tab (\ t).

Es ist nicht besser mit Path.GetInvalidPathChars Methode . Es enthält genau die gleiche Bemerkung.

René
quelle
13
Was ist dann der Sinn von Path.GetInvalidFileNameChars? Ich würde erwarten, dass es genau die ungültigen Zeichen für das aktuelle System zurückgibt, wobei ich mich darauf verlasse, dass .NET weiß, auf welchem ​​Dateisystem ich arbeite, und mir die passenden ungültigen Zeichen anzeigt. Wenn dies nicht der Fall ist und nur fest codierte Zeichen zurückgegeben werden, die überhaupt nicht zuverlässig sind, sollte diese Methode entfernt werden, da sie den Wert Null hat.
Jan
1
Ich weiß, dass dies ein alter Kommentar ist, aber @Jan, Sie könnten in ein anderes Dateisystem schreiben wollen, vielleicht gibt es deshalb eine Warnung.
fantastik78
3
@ fantastik78 guter Punkt, aber in diesem Fall möchte ich ein zusätzliches Enum-Argument haben, um meinen Remote-FS anzugeben. Wenn dies zu viel Wartungsaufwand ist (was höchstwahrscheinlich der Fall ist), ist diese gesamte Methode immer noch eine schlechte Idee, da Sie den falschen Eindruck von Sicherheit vermitteln.
Jan
1
@ Jan Ich stimme dir vollkommen zu, ich habe nur über die Warnung gestritten.
fantastik78
Interessanterweise ist dies eine Art "Blacklisting" ungültiger Zeichen. Wäre es nicht besser, hier nur die bekannten gültigen Zeichen auf die "Whitelist" zu setzen?! Erinnert mich an die blöde "Virusscanner" -Idee, anstatt erlaubte Apps auf die Whitelist zu setzen ....
Bernhard
26

Für Dateinamen:

var cleanFileName = string.Join("", fileName.Split(Path.GetInvalidFileNameChars()));

Für vollständige Pfade:

var cleanPath = string.Join("", path.Split(Path.GetInvalidPathChars()));

Beachten Sie, dass, wenn Sie dies als Sicherheitsfunktion verwenden möchten, ein robusterer Ansatz darin besteht, alle Pfade zu erweitern und dann zu überprüfen, ob der vom Benutzer angegebene Pfad tatsächlich ein untergeordnetes Element eines Verzeichnisses ist, auf das der Benutzer Zugriff haben sollte.

Lily Finley
quelle
18

Für den Anfang entfernt Trim nur Zeichen vom Anfang oder Ende der Zeichenfolge . Zweitens sollten Sie prüfen, ob Sie die anstößigen Zeichen wirklich entfernen möchten oder schnell fehlschlagen möchten, und dem Benutzer mitteilen, dass sein Dateiname ungültig ist. Meine Wahl ist die letztere, aber meine Antwort sollte Ihnen zumindest zeigen, wie man die Dinge richtig und falsch macht:

StackOverflow-Frage, die zeigt, wie überprüft wird, ob eine bestimmte Zeichenfolge ein gültiger Dateiname ist . Beachten Sie, dass Sie den regulären Ausdruck aus dieser Frage verwenden können, um Zeichen mit einem Ersatz für reguläre Ausdrücke zu entfernen (falls Sie dies wirklich tun müssen).

user7116
quelle
Ich stimme dem zweiten Rat besonders zu.
OregonGhost
4
Normalerweise würde ich dem zweiten zustimmen, aber ich habe ein Programm, das einen Dateinamen generiert und in einigen Situationen möglicherweise illegale Zeichen enthält. Da mein Programm die illegalen Dateinamen generiert, halte ich es für angebracht, diese Zeichen zu entfernen / zu ersetzen. (Nur auf einen gültigen Anwendungsfall hinweisen)
JDB erinnert sich noch an Monica
16

Der beste Weg, um unzulässige Zeichen aus Benutzereingaben zu entfernen, besteht darin, unzulässige Zeichen mithilfe der Regex-Klasse zu ersetzen, eine Methode im Code dahinter zu erstellen oder sie auf der Clientseite mithilfe des RegularExpression-Steuerelements zu validieren.

public string RemoveSpecialCharacters(string str)
{
    return Regex.Replace(str, "[^a-zA-Z0-9_]+", "_", RegexOptions.Compiled);
}

ODER

<asp:RegularExpressionValidator ID="regxFolderName" 
                                runat="server" 
                                ErrorMessage="Enter folder name with  a-z A-Z0-9_" 
                                ControlToValidate="txtFolderName" 
                                Display="Dynamic" 
                                ValidationExpression="^[a-zA-Z0-9_]*$" 
                                ForeColor="Red">
Anomepani
quelle
5
IMHO ist diese Lösung viel besser als andere. Anstatt nach allen ungültigen Zeichen zu suchen, definieren Sie einfach, welche gültig sind.
Igorushi
15

Ich benutze reguläre Ausdrücke, um dies zu erreichen. Zuerst baue ich den regulären Ausdruck dynamisch.

string regex = string.Format(
                   "[{0}]",
                   Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);

Dann rufe ich einfach removeInvalidChars.Replace auf, um das Suchen und Ersetzen durchzuführen. Dies kann natürlich auch auf Pfadzeichen ausgedehnt werden.

Jeff Yates
quelle
Seltsam, es hat bei mir funktioniert. Ich werde es noch einmal überprüfen, wenn ich die Gelegenheit dazu bekomme. Können Sie genauer sein und erklären, was genau für Sie nicht funktioniert?
Jeff Yates
1
Es wird nicht funktionieren (zumindest nicht richtig), weil Sie den Pfadzeichen nicht richtig entkommen und einige von ihnen eine besondere Bedeutung haben. Informationen dazu finden Sie in meiner Antwort.
Matthew Scharley
@ Jeff: Deine Version ist immer noch besser als die von Matthew, wenn du sie leicht modifizierst. Beziehen Sie sich auf meine Antwort, wie.
Januar
2
Ich würde auch einige andere ungültige Dateinamenmuster hinzufügen, die auf MSDN zu finden sind, und Ihre Lösung auf den folgenden new Regex(String.Format("^(CON|PRN|AUX|NUL|CLOCK\$|COM[1-9]|LPT[1-9])(?=\..|$)|(^(\.+|\s+)$)|((\.+|\s+)$)|([{0}])", Regex.Escape(new String(Path.GetInvalidFileNameChars()))), RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.CultureInvariant);
regulären Ausdruck erweitern
13

Ich bevorzuge die Idee von Jeff Yates. Es wird perfekt funktionieren, wenn Sie es leicht modifizieren:

string regex = String.Format("[{0}]", Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);

Die Verbesserung besteht nur darin, dem automatisch erzeugten regulären Ausdruck zu entkommen.

Jan.
quelle
11

Hier ist ein Code-Snippet, das für .NET 3 und höher hilfreich sein sollte.

using System.IO;
using System.Text.RegularExpressions;

public static class PathValidation
{
    private static string pathValidatorExpression = "^[^" + string.Join("", Array.ConvertAll(Path.GetInvalidPathChars(), x => Regex.Escape(x.ToString()))) + "]+$";
    private static Regex pathValidator = new Regex(pathValidatorExpression, RegexOptions.Compiled);

    private static string fileNameValidatorExpression = "^[^" + string.Join("", Array.ConvertAll(Path.GetInvalidFileNameChars(), x => Regex.Escape(x.ToString()))) + "]+$";
    private static Regex fileNameValidator = new Regex(fileNameValidatorExpression, RegexOptions.Compiled);

    private static string pathCleanerExpression = "[" + string.Join("", Array.ConvertAll(Path.GetInvalidPathChars(), x => Regex.Escape(x.ToString()))) + "]";
    private static Regex pathCleaner = new Regex(pathCleanerExpression, RegexOptions.Compiled);

    private static string fileNameCleanerExpression = "[" + string.Join("", Array.ConvertAll(Path.GetInvalidFileNameChars(), x => Regex.Escape(x.ToString()))) + "]";
    private static Regex fileNameCleaner = new Regex(fileNameCleanerExpression, RegexOptions.Compiled);

    public static bool ValidatePath(string path)
    {
        return pathValidator.IsMatch(path);
    }

    public static bool ValidateFileName(string fileName)
    {
        return fileNameValidator.IsMatch(fileName);
    }

    public static string CleanPath(string path)
    {
        return pathCleaner.Replace(path, "");
    }

    public static string CleanFileName(string fileName)
    {
        return fileNameCleaner.Replace(fileName, "");
    }
}
James
quelle
8

Die meisten der oben genannten Lösungen kombinieren unzulässige Zeichen für Pfad und Dateinamen, was falsch ist (selbst wenn beide Aufrufe derzeit denselben Zeichensatz zurückgeben). Ich würde zuerst den Pfad + Dateinamen in Pfad und Dateiname aufteilen, dann den entsprechenden Satz auf einen der beiden anwenden und dann die beiden erneut kombinieren.

wvd_vegt

wvd_vegt
quelle
+1: Sehr wahr. In .NET 4.0 hat die Regex-Lösung aus der obersten Antwort heute alle Backslashes in einem vollständigen Pfad zusammengefasst. Also habe ich einen
regulären Ausdruck
Das mag wahr sein, aber das beantwortet die Frage nicht. Ich bin mir nicht sicher, ob ein vages "Ich würde es so machen" im Vergleich zu einigen der bereits hier enthaltenen Komplettlösungen furchtbar hilfreich ist (siehe zum Beispiel Lillys Antwort unten)
Ian Grainger,
6

Wenn Sie die ungültigen Zeichen entfernen oder durch ein einzelnes Zeichen ersetzen, können Kollisionen auftreten:

<abc -> abc
>abc -> abc

Hier ist eine einfache Methode, um dies zu vermeiden:

public static string ReplaceInvalidFileNameChars(string s)
{
    char[] invalidFileNameChars = System.IO.Path.GetInvalidFileNameChars();
    foreach (char c in invalidFileNameChars)
        s = s.Replace(c.ToString(), "[" + Array.IndexOf(invalidFileNameChars, c) + "]");
    return s;
}

Das Ergebnis:

 <abc -> [1]abc
 >abc -> [2]abc
Maxence
quelle
5

Eine Ausnahme auslösen.

if ( fileName.IndexOfAny(Path.GetInvalidFileNameChars()) > -1 )
            {
                throw new ArgumentException();
            }
mirezus
quelle
4

Ich habe dieses Monster zum Spaß geschrieben, damit kannst du eine Rundreise machen:

public static class FileUtility
{
    private const char PrefixChar = '%';
    private static readonly int MaxLength;
    private static readonly Dictionary<char,char[]> Illegals;
    static FileUtility()
    {
        List<char> illegal = new List<char> { PrefixChar };
        illegal.AddRange(Path.GetInvalidFileNameChars());
        MaxLength = illegal.Select(x => ((int)x).ToString().Length).Max();
        Illegals = illegal.ToDictionary(x => x, x => ((int)x).ToString("D" + MaxLength).ToCharArray());
    }

    public static string FilenameEncode(string s)
    {
        var builder = new StringBuilder();
        char[] replacement;
        using (var reader = new StringReader(s))
        {
            while (true)
            {
                int read = reader.Read();
                if (read == -1)
                    break;
                char c = (char)read;
                if(Illegals.TryGetValue(c,out replacement))
                {
                    builder.Append(PrefixChar);
                    builder.Append(replacement);
                }
                else
                {
                    builder.Append(c);
                }
            }
        }
        return builder.ToString();
    }

    public static string FilenameDecode(string s)
    {
        var builder = new StringBuilder();
        char[] buffer = new char[MaxLength];
        using (var reader = new StringReader(s))
        {
            while (true)
            {
                int read = reader.Read();
                if (read == -1)
                    break;
                char c = (char)read;
                if (c == PrefixChar)
                {
                    reader.Read(buffer, 0, MaxLength);
                    var encoded =(char) ParseCharArray(buffer);
                    builder.Append(encoded);
                }
                else
                {
                    builder.Append(c);
                }
            }
        }
        return builder.ToString();
    }

    public static int ParseCharArray(char[] buffer)
    {
        int result = 0;
        foreach (char t in buffer)
        {
            int digit = t - '0';
            if ((digit < 0) || (digit > 9))
            {
                throw new ArgumentException("Input string was not in the correct format");
            }
            result *= 10;
            result += digit;
        }
        return result;
    }
}
Johan Larsson
quelle
1
Ich mag das, weil es vermieden wird, dass zwei verschiedene Zeichenfolgen denselben resultierenden Pfad erstellen.
Kim
3

Ich denke, es ist viel einfacher, mit einem regulären Ausdruck zu validieren und anzugeben, welche Zeichen zulässig sind, anstatt zu versuchen, nach allen schlechten Zeichen zu suchen. Siehe diese Links: http://www.c-sharpcorner.com/UploadFile/prasad_1/RegExpPSD12062005021717AM/RegExpPSD.aspx http://www.windowsdevcenter.com/pub/a/oreilly/windows/news/csharp_0101.html

Suchen Sie auch nach "Editor für reguläre Ausdrücke", sie helfen sehr. Es gibt einige, die sogar den Code in c # für Sie ausgeben.

Sandor Davidhazi
quelle
Angesichts der Tatsache, dass .net ein Framework ist, mit dem Programme auf mehreren Plattformen (z. B. Linux / Unix sowie Windows) ausgeführt werden können, halte ich Path.GetInvalidFileNameChars () für das Beste, da es das Wissen darüber enthält, was ist oder nicht. Nicht gültig auf dem Dateisystem, auf dem Ihr Programm ausgeführt wird. Selbst wenn Ihr Programm niemals unter Linux ausgeführt wird (möglicherweise ist es voll mit WPF-Code), besteht immer die Möglichkeit, dass in Zukunft ein neues Windows-Dateisystem hinzukommt und andere gültige / ungültige Zeichen enthält. Wenn Sie mit Regex Ihre eigenen Rollen spielen, erfinden Sie das Rad neu und verschieben ein Plattformproblem in Ihren eigenen Code.
Daniel Scott
Ich stimme jedoch Ihrem Rat zu Online-Regex-Editoren / -Testern zu. Ich finde sie von unschätzbarem Wert (da Regexe knifflige Dinge sind und voller Subtilität, die Sie leicht aus dem Konzept bringen kann und Ihnen einen Regex gibt, der sich bei Randfällen auf eine völlig unerwartete Weise verhält). Mein Favorit ist regex101.com (Ich mag es, wie es den Regex aufschlüsselt und Ihnen klar zeigt, was es erwartet, dass es übereinstimmt). Ich mag debuggex.com auch sehr, da es eine kompakte visuelle Darstellung von Matchgruppen und Charakterklassen und so weiter hat.
Daniel Scott
3

Dies scheint O (n) zu sein und verbraucht nicht zu viel Speicher für Zeichenfolgen:

    private static readonly HashSet<char> invalidFileNameChars = new HashSet<char>(Path.GetInvalidFileNameChars());

    public static string RemoveInvalidFileNameChars(string name)
    {
        if (!name.Any(c => invalidFileNameChars.Contains(c))) {
            return name;
        }

        return new string(name.Where(c => !invalidFileNameChars.Contains(c)).ToArray());
    }
Alexey F.
quelle
1
Ich glaube nicht, dass es O (n) ist, wenn Sie die 'Any'-Funktion verwenden.
II Pfeile
@IIARROWS und was ist es deiner Meinung nach?
Alexey F
Ich weiß nicht, es fühlte sich einfach nicht so an, als ich meinen Kommentar schrieb ... jetzt, wo ich versucht habe, ihn zu berechnen, sieht es so aus, als hättest du recht.
II Pfeile
Ich habe diesen aufgrund Ihrer Leistungsüberlegung ausgewählt. Vielen Dank.
Berend Engelbrecht
3

Beim Durchsuchen der Antworten hier scheinen alle ** die Verwendung eines char-Arrays ungültiger Dateinamenzeichen zu beinhalten.

Zugegeben, dies mag eine Mikrooptimierung sein - aber für alle, die eine große Anzahl von Werten auf gültige Dateinamen überprüfen möchten, ist es erwähnenswert, dass das Erstellen eines Hash-Sets ungültiger Zeichen zu einer deutlich besseren Leistung führt.

Ich war in der Vergangenheit sehr überrascht (schockiert), wie schnell ein Hashset (oder Wörterbuch) die Iteration über eine Liste übertrifft. Mit Strings ist es eine lächerlich niedrige Zahl (ungefähr 5-7 Elemente aus dem Speicher). Bei den meisten anderen einfachen Daten (Objektreferenzen, Zahlen usw.) scheint die magische Überkreuzung etwa 20 Elemente zu betragen.

Die "Liste" von Path.InvalidFileNameChars enthält 40 ungültige Zeichen. Habe heute eine Suche durchgeführt und es gibt hier auf StackOverflow einen ziemlich guten Benchmark, der zeigt, dass das Hashset etwas mehr als die Hälfte der Zeit eines Arrays / einer Liste für 40 Elemente benötigt: https://stackoverflow.com/a/10762995/949129

Hier ist die Hilfsklasse, mit der ich Pfade bereinige. Ich vergesse jetzt, warum ich die ausgefallene Ersatzoption darin hatte, aber es ist als süßer Bonus da.

Zusätzliche Bonusmethode "IsValidLocalPath" auch :)

(** diejenigen, die keine regulären Ausdrücke verwenden)

public static class PathExtensions
{
    private static HashSet<char> _invalidFilenameChars;
    private static HashSet<char> InvalidFilenameChars
    {
        get { return _invalidFilenameChars ?? (_invalidFilenameChars = new HashSet<char>(Path.GetInvalidFileNameChars())); }
    }


    /// <summary>Replaces characters in <c>text</c> that are not allowed in file names with the 
    /// specified replacement character.</summary>
    /// <param name="text">Text to make into a valid filename. The same string is returned if 
    /// it is valid already.</param>
    /// <param name="replacement">Replacement character, or NULL to remove bad characters.</param>
    /// <param name="fancyReplacements">TRUE to replace quotes and slashes with the non-ASCII characters ” and ⁄.</param>
    /// <returns>A string that can be used as a filename. If the output string would otherwise be empty, "_" is returned.</returns>
    public static string ToValidFilename(this string text, char? replacement = '_', bool fancyReplacements = false)
    {
        StringBuilder sb = new StringBuilder(text.Length);
        HashSet<char> invalids = InvalidFilenameChars;
        bool changed = false;

        for (int i = 0; i < text.Length; i++)
        {
            char c = text[i];
            if (invalids.Contains(c))
            {
                changed = true;
                char repl = replacement ?? '\0';
                if (fancyReplacements)
                {
                    if (c == '"') repl = '”'; // U+201D right double quotation mark
                    else if (c == '\'') repl = '’'; // U+2019 right single quotation mark
                    else if (c == '/') repl = '⁄'; // U+2044 fraction slash
                }
                if (repl != '\0')
                    sb.Append(repl);
            }
            else
                sb.Append(c);
        }

        if (sb.Length == 0)
            return "_";

        return changed ? sb.ToString() : text;
    }


    /// <summary>
    /// Returns TRUE if the specified path is a valid, local filesystem path.
    /// </summary>
    /// <param name="pathString"></param>
    /// <returns></returns>
    public static bool IsValidLocalPath(this string pathString)
    {
        // From solution at https://stackoverflow.com/a/11636052/949129
        Uri pathUri;
        Boolean isValidUri = Uri.TryCreate(pathString, UriKind.Absolute, out pathUri);
        return isValidUri && pathUri != null && pathUri.IsLoopback;
    }
}
Daniel Scott
quelle
2
public static class StringExtensions
      {
        public static string RemoveUnnecessary(this string source)
        {
            string result = string.Empty;
            string regex = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
            Regex reg = new Regex(string.Format("[{0}]", Regex.Escape(regex)));
            result = reg.Replace(source, "");
            return result;
        }
    }

Sie können die Methode klar anwenden.

Aemre
quelle
2

Dateiname kann nicht enthalten Zeichen aus Path.GetInvalidPathChars(), +und #Symbole und andere spezifische Namen. Wir haben alle Schecks in einer Klasse zusammengefasst:

public static class FileNameExtensions
{
    private static readonly Lazy<string[]> InvalidFileNameChars =
        new Lazy<string[]>(() => Path.GetInvalidPathChars()
            .Union(Path.GetInvalidFileNameChars()
            .Union(new[] { '+', '#' })).Select(c => c.ToString(CultureInfo.InvariantCulture)).ToArray());


    private static readonly HashSet<string> ProhibitedNames = new HashSet<string>
    {
        @"aux",
        @"con",
        @"clock$",
        @"nul",
        @"prn",

        @"com1",
        @"com2",
        @"com3",
        @"com4",
        @"com5",
        @"com6",
        @"com7",
        @"com8",
        @"com9",

        @"lpt1",
        @"lpt2",
        @"lpt3",
        @"lpt4",
        @"lpt5",
        @"lpt6",
        @"lpt7",
        @"lpt8",
        @"lpt9"
    };

    public static bool IsValidFileName(string fileName)
    {
        return !string.IsNullOrWhiteSpace(fileName)
            && fileName.All(o => !IsInvalidFileNameChar(o))
            && !IsProhibitedName(fileName);
    }

    public static bool IsProhibitedName(string fileName)
    {
        return ProhibitedNames.Contains(fileName.ToLower(CultureInfo.InvariantCulture));
    }

    private static string ReplaceInvalidFileNameSymbols([CanBeNull] this string value, string replacementValue)
    {
        if (value == null)
        {
            return null;
        }

        return InvalidFileNameChars.Value.Aggregate(new StringBuilder(value),
            (sb, currentChar) => sb.Replace(currentChar, replacementValue)).ToString();
    }

    public static bool IsInvalidFileNameChar(char value)
    {
        return InvalidFileNameChars.Value.Contains(value.ToString(CultureInfo.InvariantCulture));
    }

    public static string GetValidFileName([NotNull] this string value)
    {
        return GetValidFileName(value, @"_");
    }

    public static string GetValidFileName([NotNull] this string value, string replacementValue)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            throw new ArgumentException(@"value should be non empty", nameof(value));
        }

        if (IsProhibitedName(value))
        {
            return (string.IsNullOrWhiteSpace(replacementValue) ? @"_" : replacementValue) + value; 
        }

        return ReplaceInvalidFileNameSymbols(value, replacementValue);
    }

    public static string GetFileNameError(string fileName)
    {
        if (string.IsNullOrWhiteSpace(fileName))
        {
            return CommonResources.SelectReportNameError;
        }

        if (IsProhibitedName(fileName))
        {
            return CommonResources.FileNameIsProhibited;
        }

        var invalidChars = fileName.Where(IsInvalidFileNameChar).Distinct().ToArray();

        if(invalidChars.Length > 0)
        {
            return string.Format(CultureInfo.CurrentCulture,
                invalidChars.Length == 1 ? CommonResources.InvalidCharacter : CommonResources.InvalidCharacters,
                StringExtensions.JoinQuoted(@",", @"'", invalidChars.Select(c => c.ToString(CultureInfo.CurrentCulture))));
        }

        return string.Empty;
    }
}

Methode GetValidFileNameersetzt alle falschen Daten zu _.

Rücken
quelle
2

Ein Liner zum Bereinigen der Zeichenfolge von unzulässigen Zeichen für die Benennung von Windows-Dateien:

public static string CleanIllegalName(string p_testName) => new Regex(string.Format("[{0}]", Regex.Escape(new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars())))).Replace(p_testName, "");
Zananok
quelle
1
public static bool IsValidFilename(string testName)
{
    return !new Regex("[" + Regex.Escape(new String(System.IO.Path.GetInvalidFileNameChars())) + "]").IsMatch(testName);
}
mbdavis
quelle
0

Dies wird Sie wollen und Kollisionen vermeiden

 static string SanitiseFilename(string key)
    {
        var invalidChars = Path.GetInvalidFileNameChars();
        var sb = new StringBuilder();
        foreach (var c in key)
        {
            var invalidCharIndex = -1;
            for (var i = 0; i < invalidChars.Length; i++)
            {
                if (c == invalidChars[i])
                {
                    invalidCharIndex = i;
                }
            }
            if (invalidCharIndex > -1)
            {
                sb.Append("_").Append(invalidCharIndex);
                continue;
            }

            if (c == '_')
            {
                sb.Append("__");
                continue;
            }

            sb.Append(c);
        }
        return sb.ToString();

    }
mcintyre321
quelle
0

Ich denke, die Frage ist bereits nicht vollständig beantwortet ... Die Antworten beschreiben nur den sauberen Dateinamen ODER den Pfad ... nicht beide. Hier ist meine Lösung:

private static string CleanPath(string path)
{
    string regexSearch = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
    Regex r = new Regex(string.Format("[{0}]", Regex.Escape(regexSearch)));
    List<string> split = path.Split('\\').ToList();
    string returnValue = split.Aggregate(string.Empty, (current, s) => current + (r.Replace(s, "") + @"\"));
    returnValue = returnValue.TrimEnd('\\');
    return returnValue;
}
Suplanus
quelle
0

Ich habe eine Erweiterungsmethode erstellt, die mehrere Vorschläge kombiniert:

  1. Unzulässige Zeichen in einem Hash-Set halten
  2. Herausfiltern von Zeichen unter ASCII 127. Da Path.GetInvalidFileNameChars nicht alle ungültigen Zeichen enthält, die mit ASCII-Codes von 0 bis 255 möglich sind. Siehe hier und MSDN
  3. Möglichkeit, das Ersatzzeichen zu definieren

Quelle:

public static class FileNameCorrector
{
    private static HashSet<char> invalid = new HashSet<char>(Path.GetInvalidFileNameChars());

    public static string ToValidFileName(this string name, char replacement = '\0')
    {
        var builder = new StringBuilder();
        foreach (var cur in name)
        {
            if (cur > 31 && cur < 128 && !invalid.Contains(cur))
            {
                builder.Append(cur);
            }
            else if (replacement != '\0')
            {
                builder.Append(replacement);
            }
        }

        return builder.ToString();
    }
}
schoetbi
quelle
0

Hier ist eine Funktion, die alle unzulässigen Zeichen in einem Dateinamen durch ein Ersatzzeichen ersetzt:

public static string ReplaceIllegalFileChars(string FileNameWithoutPath, char ReplacementChar)
{
  const string IllegalFileChars = "*?/\\:<>|\"";
  StringBuilder sb = new StringBuilder(FileNameWithoutPath.Length);
  char c;

  for (int i = 0; i < FileNameWithoutPath.Length; i++)
  {
    c = FileNameWithoutPath[i];
    if (IllegalFileChars.IndexOf(c) >= 0)
    {
      c = ReplacementChar;
    }
    sb.Append(c);
  }
  return (sb.ToString());
}

Zum Beispiel kann der Unterstrich als Ersatzzeichen verwendet werden:

NewFileName = ReplaceIllegalFileChars(FileName, '_');
Hans-Peter Kalb
quelle
Zusätzlich zu der Antwort, die Sie gegeben haben, sollten Sie eine kurze Erklärung darüber geben, warum und wie dies das Problem behebt.
14.
-7

Oder du kannst es einfach tun

[YOUR STRING].Replace('\\', ' ').Replace('/', ' ').Replace('"', ' ').Replace('*', ' ').Replace(':', ' ').Replace('?', ' ').Replace('<', ' ').Replace('>', ' ').Replace('|', ' ').Trim();
Danny Fallas
quelle