it-swarm-eu.dev

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

Ich möchte eine Batch-Datei-Umbenennungsfunktion in meine Anwendung aufnehmen. Ein Benutzer kann ein Zieldateinamenmuster eingeben und (nachdem einige Platzhalterzeichen im Muster ersetzt wurden), muss ich überprüfen, ob es sich um einen legalen Dateinamen unter Windows handelt. Ich habe versucht, reguläre Ausdrücke wie [a-zA-Z0-9_]+ zu verwenden, 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?

150
tomash

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

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 mehr Details ein.

96
Eugene Katz

Von MSDNs "Benennen einer Datei oder eines Verzeichnisses" Hier sind die allgemeinen Konventionen für einen legalen Dateinamen unter Windows:

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

  • <>:"/\|?*
  • Zeichen, deren Ganzzahldarstellungen 0-31 sind (weniger als ASCII Leerzeichen)
  • Jedes andere Zeichen, das das Zieldateisystem nicht zulässt (z. B. nachlaufende Zeiträume oder Leerzeichen)
  • Alle DOS-Namen: CON, PRN, AUX, NUL, COM0, COM1, COM2, COM3, COM4, ​​COM5, COM6, COM7, COM8, COM9, LPT0, LPT1, LPT2, LPT3, LPT4, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9 (und vermeiden Sie AUX.txt usw.)
  • Der Dateiname besteht aus allen Perioden

Einige optionale Dinge zum Ü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 Dateiname) mit mehr als 32.000 Zeichen bei Verwendung von \?\ (Beachten Sie, dass das Präfix Verzeichniskomponenten erweitern kann und dazu führt, dass die Grenze von 32.000 überschritten wird.)
116
user7116

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

Der Abgleich von regulären Ausdrücken sollte Ihnen einen guten Weg geben. Hier ist ein Ausschnitt mit der System.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;
}

Für .Net Frameworks nach 3.0 sollte dies funktionieren:

http://msdn.Microsoft.com/de-de/library/system.io.path.getinvalidpathchars(v=vs.90).aspx

Der Abgleich von regulären Ausdrücken sollte Ihnen einen guten Weg geben. Hier ist ein Ausschnitt mit der Konstante System.IO.Path.GetInvalidPathChars();

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\drive und \\server\share\dir\file.ext.

62
Steve Cooper

Versuchen Sie es zu benutzen und fangen Sie den Fehler ab. Der erlaubte Satz kann sich in allen Dateisystemen oder in 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 es Ihnen sagen.

25

Diese Klasse reinigt Dateinamen und Pfade. benutze es gerne 

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

}
23
Steve Cooper

Das ist was ich benutze:

    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/ungültigen Dateinamen und -zeichen nur für Windows-Plattformen enthält. Der zweite macht dasselbe, stellt jedoch sicher, dass der Name für jede Plattform zulässig ist.

22
Scott Dorman

Ein Eckpunkt, den ich im Hinterkopf behalten sollte, was mich überrascht hat, als ich das erste Mal erfahren habe: Windows erlaubt führende Leerzeichen in Dateinamen! Zum Beispiel sind alle folgenden legalen und unterschiedlichen Dateinamen unter Windows (ohne Anführungszeichen):

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

Einmal etwas zum Mitnehmen: Seien Sie vorsichtig, wenn Sie Code schreiben, der führende/nachgestellte Leerzeichen aus einer Dateinamenzeichenfolge abschneidet.

18
Jon Schneider

Die Antwort von Eugene Katz vereinfachen:

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))
}
8
tmt

Microsoft Windows: Der Windows-Kernel verbietet die Verwendung von Zeichen im Bereich 1-31 (dh 0x01-0x1F) und die Zeichen "*: <>?\|". NTFS lässt zu, dass jede Pfadkomponente (Verzeichnis oder Dateiname) 255 Zeichen lang ist Pfade mit einer Länge von bis zu etwa 32767 Zeichen, der Windows-Kernel unterstützt nur Pfade mit einer Länge von bis zu 259. 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 angegeben wird.) Diese Einschränkungen gelten nur für Windows - Linux erlaubt beispielsweise die Verwendung von "*: <>?\| sogar in NTFS.

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

8
Martin Faartoft

Anstatt alle möglichen Zeichen explizit einzuschließen, können Sie einen regulären Ausdruck ausführen, um das Vorhandensein illegaler Zeichen zu überprüfen, und dann einen Fehler melden. Im Idealfall sollte Ihre Anwendung die Dateien genau so benennen, wie der Benutzer es wünscht, und nur dann schreien, wenn sie über einen Fehler stolpert.

7
ConroyP

Ich verwende das, um ungültige Zeichen in Dateinamen zu entfernen, ohne Ausnahmen zu erzeugen:

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

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

Auch CON, PRN, AUX, NUL, COM # und einige andere sind in keinem Verzeichnis mit beliebiger Erweiterung legale Dateinamen.

5
Roland Rabien

Die Frage ist, ob Sie versuchen zu ermitteln, ob ein Pfadname ein zulässiger Windows-Pfad ist oder ob es sich um einen gültigen auf dem System, auf dem der Code ausgeführt wird.? Ich denke, Letzteres ist wichtiger, also persönlich würde ich wahrscheinlich den vollständigen Pfad zerlegen und versuchen, _mkdir zu verwenden, um das Verzeichnis zu erstellen, in das die Datei gehört, und dann zu versuchen, die Datei zu erstellen.

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

5
kfh

Ergänzend zu den anderen Antworten finden Sie hier einige weitere Edge-Fälle, die Sie berücksichtigen sollten.

4
Joe

Von MSDN , hier eine Liste von Zeichen, die nicht erlaubt 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).

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

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

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

2
s n

Auch das Zieldateisystem ist wichtig.

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

2
Dominik Weber

Dies ist eine bereits beantwortete Frage, aber nur für "Andere Optionen", hier eine nicht ideale:

(nicht ideal, da die Verwendung von Exceptions als Flusskontrolle im Allgemeinen ein "schlechtes Ding" ist)

public static bool IsLegalFilename(string name)
{
    try 
    {
        var fileInfo = new FileInfo(name);
        return true;
    }
    catch
    {
        return false;
    }
}
2
JerKimball

Wenn Sie nur prüfen möchten, ob eine Zeichenfolge, die Ihren Dateinamen/Pfad enthält, ungültige Zeichen enthält, ist die schnellste Methode, die ich gefunden habe, die Verwendung von Split(), um den Dateinamen in ein Array von Teilen aufzuteilen, wo ein ungültiges Zeichen vorhanden ist. Wenn das Ergebnis nur ein Array von 1 ist, sind keine ungültigen Zeichen vorhanden. :-)

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 genannte Methoden 1.000.000 Mal in LinqPad auf einem Datei-/Pfadnamen auszuführen.

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

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

Die komplizierteren regulären Ausdrücke sind um einiges schlechter. Einige andere Optionen tun dies, z. B. die Verwendung der verschiedenen Methoden der Klasse Path, um den Dateinamen abzurufen und deren interne Überprüfung den Job erledigen zu lassen (höchstwahrscheinlich aufgrund des Overheads der Ausnahmebehandlung).

Zugegeben, es ist nicht sehr oft erforderlich, dass Sie 1 Million Dateinamen validieren müssen, daher ist eine einzige Iteration für die meisten dieser Methoden ohnehin ausreichend. Es ist jedoch immer noch ziemlich effizient und effektiv, wenn Sie nur nach ungültigen Zeichen suchen.

1
Nick Albrecht

viele dieser Antworten funktionieren nicht, wenn der Dateiname zu lang ist und in einer Umgebung vor Windows 10 ausgeführt wird. Denken Sie in ähnlicher Weise darüber nach, was Sie mit Perioden tun möchten - das Führen von Zeilen oder Nachfolgen ist technisch gültig, kann jedoch Probleme verursachen, wenn Sie nicht möchten, dass die Datei 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"));
}
1
Brent

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.GetInvalidPathChars nicht den vollständigen Satz von Zeichen zurückgibt, die in Datei- und Verzeichnisnamen ungültig sind, und natürlich gibt es noch viele weitere Feinheiten.

Also verwende 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, dass dies der sicherste Weg ist. Bitte beachten Sie auch, dass ich keine erstellten Verzeichnisse lösche.

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

1
Maxence

Meiner Meinung nach besteht die einzig richtige Antwort auf diese Frage darin, den Pfad zu verwenden und das Betriebssystem und das Dateisystem prüfen zu lassen. Andernfalls werden nur alle Validierungsregeln, die das Betriebssystem und das Dateisystem bereits verwenden, erneut implementiert (und möglicherweise nur unzureichend). Wenn diese Regeln in Zukunft geändert werden, müssen Sie Ihren Code entsprechend ändern.

0
Igor Levicki

Diese Prüfung

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

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


Verwendetes .NET Framework 4.7.1, getestet unter Windows 7.

0
Vlad

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

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;
}
0
KenR

Ein Liner für die Überprüfung von Zeichen in der Zeichenfolge:

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

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

string tagetFileFullNameToBeChecked;
try
{
  Path.GetFullPath(tagetFileFullNameToBeChecked)
}
catch(AugumentException ex)
{
  // invalid chars found
}
0
Tony Sun