Wie überprüfe ich, ob eine bestimmte Zeichenfolge unter Windows ein gültiger / gültiger Dateiname ist?

165

Ich möchte eine Funktion zum Umbenennen von Batchdateien in meine Anwendung aufnehmen. Ein Benutzer kann ein Zieldateinamenmuster eingeben und (nachdem er einige Platzhalter im Muster ersetzt hat) muss ich überprüfen, ob es sich unter Windows um einen legalen Dateinamen handelt. Ich habe versucht, reguläre Ausdrücke wie zu verwenden, [a-zA-Z0-9_]+aber es enthält nicht viele nationalspezifische Zeichen aus verschiedenen Sprachen (z. B. Umlaute usw.). Was ist der beste Weg, um eine solche Überprüfung durchzuführen?

Tomash
quelle
Ich schlage vor, einen statisch kompilierten Regex zu verwenden, wenn Sie eine der Antworten mit Regex verwenden
möchten

Antworten:

100

Sie können eine Liste ungültiger Zeichen von Path.GetInvalidPathCharsund erhalten GetInvalidFileNameChars.

UPD: Siehe Steve Coopers Vorschlag, wie man diese in einem regulären Ausdruck verwendet.

UPD2: Beachten Sie, dass gemäß dem Abschnitt "Bemerkungen" in MSDN "das von dieser Methode zurückgegebene Array nicht garantiert den vollständigen Satz von Zeichen enthält, die in Datei- und Verzeichnisnamen ungültig sind." Die Antwort von sixlettervaliables geht auf weitere Details ein.

Eugene Katz
quelle
11
Dies beantwortet die Frage nicht; Es gibt viele Zeichenfolgen, die nur aus gültigen Zeichen bestehen (z. B. "....", "CON", Zeichenfolgen mit einer Länge von Hunderten von Zeichen), die keine gültigen Dateinamen sind.
Dour High Arch
31
Hat noch jemand enttäuscht, dass MS keine Funktion / API auf Systemebene für diese Funktion bereitstellt, anstatt dass jeder Entwickler seine eigene Lösung kochen muss? Ich frage mich, ob es einen sehr guten Grund dafür gibt oder nur ein Versehen auf MS-Seite.
Thomas Nguyen
@High Arch: Siehe Antwort auf Frage "Überprüfen Sie in C #, ob der Dateiname möglicherweise gültig ist (nicht, dass er existiert)". (Obwohl einige kluge Kerle diese Frage zugunsten dieser geschlossen haben ...)
mmmmmmmm
129

In MSDNs "Benennen einer Datei oder eines Verzeichnisses" finden Sie hier die allgemeinen Konventionen für die Verwendung eines legalen Dateinamens unter Windows:

Sie können ein beliebiges Zeichen in der aktuellen Codepage verwenden (Unicode / ANSI über 127), außer:

  • < > : " / \ | ? *
  • Zeichen, deren ganzzahlige Darstellungen 0-31 sind (weniger als ASCII-Leerzeichen)
  • Alle anderen Zeichen, die das Zieldateisystem nicht zulässt (z. B. nachgestellte Punkte oder Leerzeichen)
  • Beliebiger DOS-Name: CON, PRN, AUX, NUL, COM0, COM1, COM2, COM3, COM4, ​​COM5, COM6, COM7, COM8, COM9, LPT0, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9 (und vermeiden Sie AUX.txt usw.)
  • Der Dateiname ist alle Punkte

Einige optionale Dinge zu überprüfen:

  • Dateipfade (einschließlich des Dateinamens) dürfen nicht mehr als 260 Zeichen enthalten (die das \?\Präfix nicht verwenden ).
  • Unicode-Dateipfade (einschließlich des Dateinamens) mit mehr als 32.000 Zeichen bei Verwendung \?\(beachten Sie, dass das Präfix Verzeichniskomponenten erweitern und dazu führen kann, dass das Limit von 32.000 überschritten wird)
user7116
quelle
8
+1 für das Einschließen reservierter Dateinamen - diese wurden in früheren Antworten übersehen.
SqlRyan
2
"AUX" ist ein perfekt verwendbarer Dateiname, wenn Sie die Syntax "\\? \" Verwenden. Natürlich haben Programme, die diese Syntax nicht verwenden, echte Probleme damit ... (Getestet unter XP)
user9876
9
Die korrekte Regex für alle diese oben genannten Bedingungen ist wie Regex unspupportedRegex = new Regex("(^(PRN|AUX|NUL|CON|COM[1-9]|LPT[1-9]|(\\.+)$)(\\..*)?$)|(([\\x00-\\x1f\\\\?*:\";|/<>])+)|(([\\. ]+)", RegexOptions.IgnoreCase);
folgt
4
@whywhywhy Ich denke, Sie haben eine zusätzliche Öffnungsklammer in diesem Regex. "(^ (PRN | AUX | NUL | CON | COM [1-9] | LPT [1-9] | (\\. +) $) (\\ .. *)? $) | (([\\ x00 - \\ x1f \\\\? *: \ "; ‌ | / <>]) +) | ([\\.] +)" arbeitete für mich.
Wilky
4
Ich habe den gleichen Artikel gelesen, der in dieser Antwort erwähnt wurde, und durch Experimente festgestellt, dass COM0 und LPT0 ebenfalls nicht erlaubt sind. @dlf dieser arbeitet mit Dateinamen, die mit '.' beginnen:^(?!^(?:PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d)(?:\..+)?$)(?:\.*?(?!\.))[^\x00-\x1f\\?*:\";|\/<>]+(?<![\s.])$
mjohnsonengr
67

Für .Net Frameworks vor 3.5 sollte dies funktionieren:

Der Abgleich regulärer Ausdrücke sollte Ihnen einen Teil des Weges erleichtern. Hier ist ein Ausschnitt mit demSystem.IO.Path.InvalidPathChars Konstante.

bool IsValidFilename(string testName)
{
    Regex containsABadCharacter = new Regex("[" 
          + Regex.Escape(System.IO.Path.InvalidPathChars) + "]");
    if (containsABadCharacter.IsMatch(testName)) { return false; };

    // other checks for UNC, drive-path format, etc

    return true;
}

Zum .Net Frameworks nach 3.0 sollte dies funktionieren:

http://msdn.microsoft.com/en-us/library/system.io.path.getinvalidpathchars(v=vs.90).aspx

Der Abgleich regulärer Ausdrücke sollte Ihnen einen Teil des Weges erleichtern. Hier ist ein Ausschnitt mit derSystem.IO.Path.GetInvalidPathChars() Konstante.

bool IsValidFilename(string testName)
{
    Regex containsABadCharacter = new Regex("["
          + Regex.Escape(new string(System.IO.Path.GetInvalidPathChars())) + "]");
    if (containsABadCharacter.IsMatch(testName)) { return false; };

    // other checks for UNC, drive-path format, etc

    return true;
}

Sobald Sie das wissen, sollten Sie auch nach verschiedenen Formaten suchen, z. B. c:\my\driveund\\server\share\dir\file.ext

Steve Cooper
quelle
Testet dies nicht nur den Pfad, nicht den Dateinamen?
Eugene Katz
30
Zeichenfolge strTheseAreInvalidFileNameChars = neue Zeichenfolge (System.IO.Path.GetInvalidFileNameChars ()); Regex regFixFileName = neuer Regex ("[" + Regex.Escape (strTheseAreInvalidFileNameChars) + "]");
Rao
2
Ein wenig Forschung von Menschen würde Wunder wirken. Ich habe den Beitrag aktualisiert, um die Änderungen widerzuspiegeln.
Erik Philips
1
2. Code wird nicht kompiliert. "Kann nicht von char [] in string konvertieren
Paul Hunt
1
@AshkanMobayenKhiabani: InvalidPathChars ist veraltet, GetInvalidPathChars jedoch nicht.
IvanH
25

Versuchen Sie es zu verwenden und suchen Sie nach dem Fehler. Der zulässige Satz kann sich zwischen Dateisystemen oder zwischen verschiedenen Windows-Versionen ändern. Mit anderen Worten, wenn Sie wissen möchten, ob Windows den Namen mag, geben Sie ihm den Namen und lassen Sie sich davon erzählen.


quelle
1
Dies scheint der einzige zu sein, der gegen alle Einschränkungen testet. Warum werden die anderen Antworten darüber gewählt?
Lücke
5
@gap weil es nicht immer funktioniert. Beispielsweise ist der Versuch, auf CON zuzugreifen, häufig erfolgreich, obwohl es sich nicht um eine echte Datei handelt.
Antimon
4
Es ist jedoch immer besser, den Speicheraufwand beim Auslösen einer Ausnahme zu vermeiden, sofern dies möglich ist.
Owen Blacker
2
Möglicherweise haben Sie auch keine Berechtigung, darauf zuzugreifen. zB um es schriftlich zu testen, auch wenn Sie es lesen können, wenn es existiert oder existieren wird.
CodeLurker
23

Diese Klasse bereinigt Dateinamen und Pfade. benutze es wie

var myCleanPath = PathSanitizer.SanitizeFilename(myBadPath, ' ');

Hier ist der Code;

/// <summary>
/// Cleans paths of invalid characters.
/// </summary>
public static class PathSanitizer
{
    /// <summary>
    /// The set of invalid filename characters, kept sorted for fast binary search
    /// </summary>
    private readonly static char[] invalidFilenameChars;
    /// <summary>
    /// The set of invalid path characters, kept sorted for fast binary search
    /// </summary>
    private readonly static char[] invalidPathChars;

    static PathSanitizer()
    {
        // set up the two arrays -- sorted once for speed.
        invalidFilenameChars = System.IO.Path.GetInvalidFileNameChars();
        invalidPathChars = System.IO.Path.GetInvalidPathChars();
        Array.Sort(invalidFilenameChars);
        Array.Sort(invalidPathChars);

    }

    /// <summary>
    /// Cleans a filename of invalid characters
    /// </summary>
    /// <param name="input">the string to clean</param>
    /// <param name="errorChar">the character which replaces bad characters</param>
    /// <returns></returns>
    public static string SanitizeFilename(string input, char errorChar)
    {
        return Sanitize(input, invalidFilenameChars, errorChar);
    }

    /// <summary>
    /// Cleans a path of invalid characters
    /// </summary>
    /// <param name="input">the string to clean</param>
    /// <param name="errorChar">the character which replaces bad characters</param>
    /// <returns></returns>
    public static string SanitizePath(string input, char errorChar)
    {
        return Sanitize(input, invalidPathChars, errorChar);
    }

    /// <summary>
    /// Cleans a string of invalid characters.
    /// </summary>
    /// <param name="input"></param>
    /// <param name="invalidChars"></param>
    /// <param name="errorChar"></param>
    /// <returns></returns>
    private static string Sanitize(string input, char[] invalidChars, char errorChar)
    {
        // null always sanitizes to null
        if (input == null) { return null; }
        StringBuilder result = new StringBuilder();
        foreach (var characterToTest in input)
        {
            // we binary search for the character in the invalid set. This should be lightning fast.
            if (Array.BinarySearch(invalidChars, characterToTest) >= 0)
            {
                // we found the character in the array of 
                result.Append(errorChar);
            }
            else
            {
                // the character was not found in invalid, so it is valid.
                result.Append(characterToTest);
            }
        }

        // we're done.
        return result.ToString();
    }

}
Steve Cooper
quelle
1
Ihre Antwort könnte hier besser passen: stackoverflow.com/questions/146134/…
nawfal
22

Das benutze ich:

    public static bool IsValidFileName(this string expression, bool platformIndependent)
    {
        string sPattern = @"^(?!^(PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d|\..*)(\..+)?$)[^\x00-\x1f\\?*:\"";|/]+$";
        if (platformIndependent)
        {
           sPattern = @"^(([a-zA-Z]:|\\)\\)?(((\.)|(\.\.)|([^\\/:\*\?""\|<>\. ](([^\\/:\*\?""\|<>\. ])|([^\\/:\*\?""\|<>]*[^\\/:\*\?""\|<>\. ]))?))\\)*[^\\/:\*\?""\|<>\. ](([^\\/:\*\?""\|<>\. ])|([^\\/:\*\?""\|<>]*[^\\/:\*\?""\|<>\. ]))?$";
        }
        return (Regex.IsMatch(expression, sPattern, RegexOptions.CultureInvariant));
    }

Das erste Muster erstellt einen regulären Ausdruck, der die ungültigen / unzulässigen Dateinamen und Zeichen nur für Windows-Plattformen enthält. Der zweite macht dasselbe, stellt jedoch sicher, dass der Name für jede Plattform legal ist.

Scott Dorman
quelle
4
sPattern Regex erlaubt keine Dateien, die mit einem Punktzeichen gestartet wurden. Aber MSDN sagt „es akzeptabel ist , eine Periode als das erste Zeichen eines Namens angeben. Zum Beispiel : “ .temp „“. Ich würde "\ .. *" entfernen, um .gitignore korrekten Dateinamen zu machen :)
yar_shukan
(Ich habe dies schrittweise verbessert und die vorherigen Kommentare gelöscht, die ich hinterlassen habe.) Dieser ist besser als der reguläre Ausdruck der Antwort, da er ".gitignore", "..asdf" zulässt, "<" und ">" oder den Yen nicht zulässt Zeichen, und erlaubt kein Leerzeichen oder Punkt am Ende (was Namen verbietet, die nur aus Punkten bestehen):@"^(?!(?:PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d)(?:\..+)?$)[^\x00-\x1F\xA5\\?*:\"";|\/<>]+(?<![\s.])$"
mjohnsonengr
Dies schlägt für alle von mir getesteten Dateien fehl. Wenn Sie es für C: \ Windows \ System32 \ msxml6.dll ausführen, wird false gemeldet.
magicandre1981
@ magicandre1981 Sie müssen nur den Dateinamen angeben, nicht den vollständig qualifizierten Pfad.
Scott Dorman
ok, aber ich muss überprüfen, ob der vollständige Pfad gültig ist. Ich habe jetzt eine andere Lösung verwendet.
magicandre1981
18

Ein Eckfall, den ich beachten sollte, der mich überraschte, als ich zum ersten Mal davon erfuhr: Windows erlaubt führende Leerzeichen in Dateinamen! Zum Beispiel sind die folgenden alle legalen und unterschiedlichen Dateinamen unter Windows (abzüglich der Anführungszeichen):

"file.txt"
" file.txt"
"  file.txt"

Eine Erkenntnis daraus: Seien Sie vorsichtig, wenn Sie Code schreiben, der führende / nachfolgende Leerzeichen von einer Dateinamenzeichenfolge trimmt.

Jon Schneider
quelle
10

Vereinfachung der Antwort von Eugene Katz:

bool IsFileNameCorrect(string fileName){
    return !fileName.Any(f=>Path.GetInvalidFileNameChars().Contains(f))
}

Oder

bool IsFileNameCorrect(string fileName){
    return fileName.All(f=>!Path.GetInvalidFileNameChars().Contains(f))
}
tmt
quelle
Meinten Sie: "return! FileName.Any (f => Path.GetInvalidFileNameChars (). Contains (f));" ?
Jack Griffin
@ JackGriffin Natürlich! Vielen Dank für Ihre Aufmerksamkeit.
tmt
Obwohl dieser Code sehr schön zu lesen ist, sollten wir die traurigen Interna von berücksichtigen Path.GetInvalidFileNameChars. Schauen Sie hier nach: referencesource.microsoft.com/#mscorlib/system/io/path.cs,289 - für jedes Zeichen von Ihnen fileNamewird ein Klon des Arrays erstellt.
Piotr Zierhoffer
"DD: \\\\\ AAA ..... AAAA". Nicht gültig, aber für Ihren Code ist es.
Ciccio Pasticcio
8

Microsoft Windows: Der Windows-Kernel verbietet die Verwendung von Zeichen im Bereich 1-31 (dh 0x01-0x1F) und Zeichen "*: <>? \ |. Obwohl NTFS zulässt, dass jede Pfadkomponente (Verzeichnis oder Dateiname) 255 Zeichen lang und länger ist Pfade mit einer Länge von bis zu 32767 Zeichen, der Windows-Kernel unterstützt nur Pfade mit einer Länge von bis zu 259 Zeichen. Außerdem verbietet Windows die Verwendung der MS-DOS-Gerätenamen AUX, CLOCK $, COM1, COM2, COM3, COM4, ​​COM5, COM6, COM7, COM8, COM9, CON, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9, NUL und PRN sowie diese Namen mit einer beliebigen Erweiterung (z. B. AUX.txt), außer bei Verwendung Lange UNC-Pfade (z. B. \. \ C: \ nul.txt oder \? \ D: \ aux \ con). (Tatsächlich kann CLOCK $ verwendet werden, wenn eine Erweiterung bereitgestellt wird.) Diese Einschränkungen gelten nur für Windows - Linux erlaubt zum Beispiel die Verwendung von "*: <>? \ | sogar in NTFS.

Quelle: http://en.wikipedia.org/wiki/Filename

Martin Faartoft
quelle
1
Ich kann eine Datei mit dem Namen "CLOCK $" erstellen. Windows 7.
rory.ap
7

Anstatt alle möglichen Zeichen explizit einzuschließen, können Sie eine Regex durchführen, um das Vorhandensein unzulässiger Zeichen zu überprüfen und dann einen Fehler zu melden. Im Idealfall sollte Ihre Anwendung die Dateien genau so benennen, wie es der Benutzer wünscht, und nur dann schlecht weinen, wenn ein Fehler auftritt.

ConroyP
quelle
6

Die Frage ist, ob Sie versuchen festzustellen, ob ein Pfadname ein zulässiger Windows-Pfad ist oder ob er auf dem System, auf dem der Code ausgeführt wird, zulässig ist.? Ich denke, letzteres ist wichtiger, also würde ich persönlich wahrscheinlich den vollständigen Pfad zerlegen und versuchen, mit _mkdir das Verzeichnis zu erstellen, in das die Datei gehört, und dann versuchen, die Datei zu erstellen.

Auf diese Weise wissen Sie nicht nur, ob der Pfad nur gültige Windows-Zeichen enthält, sondern auch, ob er tatsächlich einen Pfad darstellt, der von diesem Prozess geschrieben werden kann.

kfh
quelle
6

Ich benutze dies, um ungültige Zeichen in Dateinamen zu entfernen, ohne Ausnahmen auszulösen:

private static readonly Regex InvalidFileRegex = new Regex(
    string.Format("[{0}]", Regex.Escape(@"<>:""/\|?*")));

public static string SanitizeFileName(string fileName)
{
    return InvalidFileRegex.Replace(fileName, string.Empty);
}
JoelFan
quelle
5

Auch CON, PRN, AUX, NUL, COM # und einige andere sind niemals legale Dateinamen in einem Verzeichnis mit einer Erweiterung.


quelle
1
Dies ist nur die halbe Wahrheit. Sie können Dateien mit diesen Namen erstellen, wenn Sie die Unicode-Version von CreateFile aufrufen (wobei dem Dateinamen "\\? \" Vorangestellt wird).
Werner Henze
Diese Aussage ist unvollständig und verfehlt LPT #
Thomas Weller
4

Um die anderen Antworten zu ergänzen, finden Sie hier einige zusätzliche Randfälle, die Sie möglicherweise berücksichtigen möchten.

Joe
quelle
3

In MSDN finden Sie eine Liste von Zeichen, die nicht zulässig sind:

Verwenden Sie fast alle Zeichen auf der aktuellen Codepage für einen Namen, einschließlich Unicode-Zeichen und Zeichen im erweiterten Zeichensatz (128–255), mit Ausnahme der folgenden:

  • Die folgenden reservierten Zeichen sind nicht zulässig: <>: "/ \ |? *
  • Zeichen, deren ganzzahlige Darstellungen im Bereich von Null bis 31 liegen, sind nicht zulässig.
  • Alle anderen Zeichen, die das Zieldateisystem nicht zulässt.
Mark Biek
quelle
2

Auch das Zieldateisystem ist wichtig.

Unter NTFS können einige Dateien nicht in bestimmten Verzeichnissen erstellt werden. EG $ Boot in root

Dominik Weber
quelle
2
Sicherlich liegt das nicht an einer NTFS-Namensregel, sondern nur daran, dass eine aufgerufene Datei $Bootbereits im Verzeichnis vorhanden ist?
Christian Hayter
2

Dies ist eine bereits beantwortete Frage, aber nur aus Gründen der "anderen Optionen" ist hier eine nicht ideale:

(Nicht ideal, da die Verwendung von Ausnahmen als Flusskontrolle im Allgemeinen eine "schlechte Sache" ist.)

public static bool IsLegalFilename(string name)
{
    try 
    {
        var fileInfo = new FileInfo(name);
        return true;
    }
    catch
    {
        return false;
    }
}
JerKimball
quelle
Ihr Beispiel hat für eine CON-Datei (C: \ temp \ CON) nicht funktioniert.
Tcbrazil
Aber ist 'C: \ temp \ CON' kein gültiger Dateiname? Warum sollte es nicht sein?
Mark A. Donohoe
@MarqueIV - nein, es ist nicht gültig. Lesen Sie alle Antworten und Kommentare oben oder probieren Sie es selbst aus und sehen Sie.
rory.ap
@Jer, "/ example" ist nicht legal, aber Ihre Methode wird zurückgegeben true.
rory.ap
Aaaah ... Ich habe den 'CON'-Teil verpasst. Der Name selbst ist unter dem Gesichtspunkt der Zeichenfolge gültig (worauf ich mich bezog), aber ich sehe jetzt, dass CON ein reservierter Name ist, was ihn unter dem Gesichtspunkt von Windows ungültig macht. Mein Fehler.
Mark A. Donohoe
2

Reguläre Ausdrücke sind für diese Situation übertrieben. Sie können die String.IndexOfAny()Methode in Kombination mit Path.GetInvalidPathChars()und verwenden Path.GetInvalidFileNameChars().

Beachten Sie auch, dass beide Path.GetInvalidXXX()Methoden ein internes Array klonen und den Klon zurückgeben. Wenn Sie dies also häufig tun (tausende und tausende Male), können Sie eine Kopie des ungültigen Zeichenarrays zur Wiederverwendung zwischenspeichern.

nhahtdh
quelle
2

Wenn Sie nur versuchen zu überprüfen, ob eine Zeichenfolge, die Ihren Dateinamen / Pfad enthält, ungültige Zeichen enthält, besteht die schnellste Methode, die ich gefunden habe, darin, Split()den Dateinamen in ein Array von Teilen aufzuteilen, wo immer ein ungültiges Zeichen vorhanden ist. Wenn das Ergebnis nur ein Array von 1 ist, gibt es keine ungültigen Zeichen. :-)

var nameToTest = "Best file name \"ever\".txt";
bool isInvalidName = nameToTest.Split(System.IO.Path.GetInvalidFileNameChars()).Length > 1;

var pathToTest = "C:\\My Folder <secrets>\\";
bool isInvalidPath = pathToTest.Split(System.IO.Path.GetInvalidPathChars()).Length > 1;

Ich habe versucht, diese und andere oben erwähnte Methoden 1.000.000 Mal in LinqPad für einen Datei- / Pfadnamen auszuführen.

Die Verwendung Split()beträgt nur ~ 850 ms.

Die Verwendung Regex("[" + Regex.Escape(new string(System.IO.Path.GetInvalidPathChars())) + "]")dauert ca. 6 Sekunden.

Die komplizierteren regulären Ausdrücke sind VIEL schlimmer, ebenso wie einige der anderen Optionen, wie die Verwendung der verschiedenen Methoden für die PathKlasse, um den Dateinamen abzurufen und ihre interne Validierung den Job erledigen zu lassen (höchstwahrscheinlich aufgrund des Overheads der Ausnahmebehandlung).

Zugegeben, es ist nicht sehr häufig, dass Sie 1 Million Dateinamen validieren müssen, daher ist eine einzelne Iteration für die meisten dieser Methoden ohnehin in Ordnung. Aber es ist immer noch ziemlich effizient und effektiv, wenn Sie nur nach ungültigen Zeichen suchen.

Nick Albrecht
quelle
1

Viele dieser Antworten funktionieren nicht, wenn der Dateiname zu lang ist und in einer Umgebung vor Windows 10 ausgeführt wird. Denken Sie auch darüber nach, was Sie mit Punkten tun möchten. Das Zulassen von führenden oder nachfolgenden Zeichen ist technisch gültig, kann jedoch zu Problemen führen, wenn die Datei nicht schwer zu sehen bzw. zu löschen ist.

Dies ist ein Validierungsattribut, das ich erstellt habe, um nach einem gültigen Dateinamen zu suchen.

public class ValidFileNameAttribute : ValidationAttribute
{
    public ValidFileNameAttribute()
    {
        RequireExtension = true;
        ErrorMessage = "{0} is an Invalid Filename";
        MaxLength = 255; //superseeded in modern windows environments
    }
    public override bool IsValid(object value)
    {
        //http://stackoverflow.com/questions/422090/in-c-sharp-check-that-filename-is-possibly-valid-not-that-it-exists
        var fileName = (string)value;
        if (string.IsNullOrEmpty(fileName)) { return true;  }
        if (fileName.IndexOfAny(Path.GetInvalidFileNameChars()) > -1 ||
            (!AllowHidden && fileName[0] == '.') ||
            fileName[fileName.Length - 1]== '.' ||
            fileName.Length > MaxLength)
        {
            return false;
        }
        string extension = Path.GetExtension(fileName);
        return (!RequireExtension || extension != string.Empty)
            && (ExtensionList==null || ExtensionList.Contains(extension));
    }
    private const string _sepChar = ",";
    private IEnumerable<string> ExtensionList { get; set; }
    public bool AllowHidden { get; set; }
    public bool RequireExtension { get; set; }
    public int MaxLength { get; set; }
    public string AllowedExtensions {
        get { return string.Join(_sepChar, ExtensionList); } 
        set {
            if (string.IsNullOrEmpty(value))
            { ExtensionList = null; }
            else {
                ExtensionList = value.Split(new char[] { _sepChar[0] })
                    .Select(s => s[0] == '.' ? s : ('.' + s))
                    .ToList();
            }
    } }

    public override bool RequiresValidationContext => false;
}

und die Tests

[TestMethod]
public void TestFilenameAttribute()
{
    var rxa = new ValidFileNameAttribute();
    Assert.IsFalse(rxa.IsValid("pptx."));
    Assert.IsFalse(rxa.IsValid("pp.tx."));
    Assert.IsFalse(rxa.IsValid("."));
    Assert.IsFalse(rxa.IsValid(".pp.tx"));
    Assert.IsFalse(rxa.IsValid(".pptx"));
    Assert.IsFalse(rxa.IsValid("pptx"));
    Assert.IsFalse(rxa.IsValid("a/abc.pptx"));
    Assert.IsFalse(rxa.IsValid("a\\abc.pptx"));
    Assert.IsFalse(rxa.IsValid("c:abc.pptx"));
    Assert.IsFalse(rxa.IsValid("c<abc.pptx"));
    Assert.IsTrue(rxa.IsValid("abc.pptx"));
    rxa = new ValidFileNameAttribute { AllowedExtensions = ".pptx" };
    Assert.IsFalse(rxa.IsValid("abc.docx"));
    Assert.IsTrue(rxa.IsValid("abc.pptx"));
}
Brent
quelle
1

Mein Versuch:

using System.IO;

static class PathUtils
{
  public static string IsValidFullPath([NotNull] string fullPath)
  {
    if (string.IsNullOrWhiteSpace(fullPath))
      return "Path is null, empty or white space.";

    bool pathContainsInvalidChars = fullPath.IndexOfAny(Path.GetInvalidPathChars()) != -1;
    if (pathContainsInvalidChars)
      return "Path contains invalid characters.";

    string fileName = Path.GetFileName(fullPath);
    if (fileName == "")
      return "Path must contain a file name.";

    bool fileNameContainsInvalidChars = fileName.IndexOfAny(Path.GetInvalidFileNameChars()) != -1;
    if (fileNameContainsInvalidChars)
      return "File name contains invalid characters.";

    if (!Path.IsPathRooted(fullPath))
      return "The path must be absolute.";

    return "";
  }
}

Dies ist nicht perfekt, da Path.GetInvalidPathCharsnicht der gesamte Zeichensatz zurückgegeben wird, der in Datei- und Verzeichnisnamen ungültig ist, und natürlich gibt es noch viel mehr Feinheiten.

Also benutze ich diese Methode als Ergänzung:

public static bool TestIfFileCanBeCreated([NotNull] string fullPath)
{
  if (string.IsNullOrWhiteSpace(fullPath))
    throw new ArgumentException("Value cannot be null or whitespace.", "fullPath");

  string directoryName = Path.GetDirectoryName(fullPath);
  if (directoryName != null) Directory.CreateDirectory(directoryName);
  try
  {
    using (new FileStream(fullPath, FileMode.CreateNew)) { }
    File.Delete(fullPath);
    return true;
  }
  catch (IOException)
  {
    return false;
  }
}

Es wird versucht, die Datei zu erstellen und false zurückzugeben, wenn eine Ausnahme vorliegt. Natürlich muss ich die Datei erstellen, aber ich denke, das ist der sicherste Weg, dies zu tun. Bitte beachten Sie auch, dass ich keine erstellten Verzeichnisse lösche.

Sie können auch die erste Methode verwenden, um eine grundlegende Validierung durchzuführen, und dann die Ausnahmen sorgfältig behandeln, wenn der Pfad verwendet wird.

Maxence
quelle
0

Ich schlage vor, nur den Path.GetFullPath () zu verwenden

string tagetFileFullNameToBeChecked;
try
{
  Path.GetFullPath(tagetFileFullNameToBeChecked)
}
catch(AugumentException ex)
{
  // invalid chars found
}
Tony Sun.
quelle
Fügen Sie eine Erklärung mit Antwort hinzu, wie diese Antwort OP bei der Behebung des aktuellen Problems
hilft
Im Dokument in der MSDN finden Sie die AugumentExcpetion. Sie lautet: Pfad ist eine Zeichenfolge mit der Länge Null, enthält nur Leerzeichen oder enthält eines oder mehrere der in GetInvalidPathChars definierten ungültigen Zeichen. -oder- Das System konnte den absoluten Pfad nicht abrufen.
Tony Sun
Theoretisch (laut Dokumentation) sollte dies funktionieren, das Problem ist jedoch zumindest in .NET Core 3.1 nicht der Fall.
Michel Jansson
0

Ich habe diese Idee von jemandem bekommen. - Ich weiß nicht wer. Lassen Sie das Betriebssystem das schwere Heben erledigen.

public bool IsPathFileNameGood(string fname)
{
    bool rc = Constants.Fail;
    try
    {
        this._stream = new StreamWriter(fname, true);
        rc = Constants.Pass;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Problem opening file");
        rc = Constants.Fail;
    }
    return rc;
}
KenR
quelle
0

Diese Prüfung

static bool IsValidFileName(string name)
{
    return
        !string.IsNullOrWhiteSpace(name) &&
        name.IndexOfAny(Path.GetInvalidFileNameChars()) < 0 &&
        !Path.GetFullPath(name).StartsWith(@"\\.\");
}

out Namen Filter mit ungültigen Zeichen ( <>:"/\|?*und ASCII 0-31), sowie reservierten DOS - Geräte ( CON, NUL, COMx). Es erlaubt führende Leerzeichen und All-Dot-Namen, die mit übereinstimmen Path.GetFullPath. (Das Erstellen einer Datei mit führenden Leerzeichen ist auf meinem System erfolgreich.)


Verwendet .NET Framework 4.7.1, getestet unter Windows 7.

Vlad
quelle
0

Ein Liner zur Überprüfung illigaler Zeichen in der Zeichenfolge:

public static bool IsValidFilename(string testName) => !Regex.IsMatch(testName, "[" + Regex.Escape(new string(System.IO.Path.InvalidPathChars)) + "]");
Zananok
quelle
0

Meiner Meinung nach besteht die einzig richtige Antwort auf diese Frage darin, zu versuchen, den Pfad zu verwenden und das Betriebssystem und das Dateisystem ihn validieren zu lassen. Andernfalls implementieren Sie nur alle Validierungsregeln, die das Betriebssystem und das Dateisystem bereits verwenden, neu (und wahrscheinlich nur unzureichend). Wenn diese Regeln in Zukunft geändert werden, müssen Sie Ihren Code entsprechend ändern.

Igor Levicki
quelle
-1

Windows - Dateinamen sind ziemlich unrestrictive, so wirklich es nicht einmal sein könnte , dass viel von einem Problem. Die von Windows nicht zugelassenen Zeichen sind:

\ / : * ? " < > |

Sie können leicht einen Ausdruck schreiben, um zu überprüfen, ob diese Zeichen vorhanden sind. Eine bessere Lösung wäre jedoch, zu versuchen, die Dateien so zu benennen, wie der Benutzer es wünscht, und sie zu benachrichtigen, wenn ein Dateiname nicht haftet.

Justin Poliey
quelle
Auch Zeichen <= 31 sind verboten.
Antimon