Comment puis-je vérifier si une chaîne de caractères donnée est un nom de fichier légal/valide sous Windows?

je veux inclure une fonctionnalité de renommage de fichier par lot dans mon application. Un utilisateur peut taper un modèle de nom de fichier de destination et (après avoir remplacé quelques caractères génériques dans le modèle) je dois vérifier si ce sera un nom de fichier légal sous Windows. J'ai essayé d'utiliser une expression régulière comme [a-zA-Z0-9_]+ mais elle ne comprend pas beaucoup de caractères spécifiques à un pays et provenant de différentes langues (par exemple umlauts et ainsi de suite). Quelle est la meilleure façon de faire cette vérification?

149
demandé sur Luke Girvin 2008-09-15 17:17:29

25 réponses

vous pouvez obtenir une liste de caractères invalides de Path.GetInvalidPathChars et GetInvalidFileNameChars .

UPD: Voir Steve Cooper suggestion sur la façon de les utiliser dans une expression régulière.

UPD2: notez que selon la section de remarques dans MSDN "le tableau retourné de cette méthode n'est pas garanti de contenir le tableau complet ensemble de caractères invalides dans les noms de fichiers et de répertoires." La réponse fournie par sixlettervaliables va plus dans les détails.

94
répondu Eugene Katz 2017-05-23 11:54:44

à Partir de MSDN "Appellation d'un Fichier ou d'un Répertoire," voici les conventions générales pour ce légal de nom de fichier est sous Windows:

vous pouvez utiliser n'importe quel caractère de la page de code courante (Unicode/ANSI ci-dessus 127), sauf:

  • < > : " / \ | ? *
  • caractères dont l'entier les représentations sont de 0 à 31 (moins d'espace ASCII)
  • tout autre caractère que le système de fichiers cible ne permet pas (par exemple, les périodes ou les espaces de fuite)
  • tous les noms de DOS: CON, PRN, AUX, NUL, COM0, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT0, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9 (et éviter AUX.TXT, etc)
  • le nom du fichier est toutes les périodes

Certains choses à vérifier en option:

  • les chemins de fichier (y compris le nom du fichier) ne peuvent pas avoir plus de 260 caractères (qui n'utilisent pas le préfixe \?\ )
  • Unicode chemins d'accès aux fichiers (y compris le nom de fichier) avec plus de 32 000 caractères lors de l'utilisation de \?\ (notez que le préfixe peut développer les composants d'annuaire et de provoquer de dépassement de la limite de 32 000)
113
répondu user7116 2017-08-25 17:27:34

pour . Cadres nets antérieurs à 3.5 cela devrait fonctionner:

l'appariement régulier des expressions devrait vous permettre de trouver quelque chose. Voici un extrait utilisant la constante System.IO.Path.InvalidPathChars ;

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

pour Cadres nets après 3.0 cela devrait fonctionner:

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

l'appariement régulier des expressions devrait vous permettre de trouver quelque chose. Voici un extrait utilisant la constante System.IO.Path.GetInvalidPathChars() ; 151990920"

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

une fois que vous savez que, vous devriez également vérifier pour différents formats, par exemple c:\my\drive et \server\share\dir\file.ext

59
répondu Steve Cooper 2015-06-16 03:10:22

essayez de l'utiliser, et traquez l'erreur. L'ensemble autorisé peut changer à travers les systèmes de fichiers, ou à travers différentes versions de Windows. En d'autres mots, si vous voulez savoir si Windows aime le nom, à part le nom et le laisser vous dire.

25
répondu 2008-09-15 14:00:49

cette classe nettoie les noms de fichiers et les chemins; utilisez-les comme

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

voici le 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
répondu Steve Cooper 2010-08-06 16:16:57

C'est ce que j'utilise:

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

le premier motif crée une expression régulière contenant les noms et les caractères de fichiers invalides/illégaux pour les plates-formes Windows uniquement. Le second fait de même mais s'assure que le nom est légal pour toute plate-forme.

22
répondu Scott Dorman 2013-04-30 08:58:59

un cas de coin à garder à l'esprit, ce qui m'a surpris quand j'ai découvert pour la première fois à ce sujet: Windows permet de diriger l'espace des caractères dans les noms de fichiers! Par exemple, les noms de fichiers suivants sont tous légaux, et distincts, sur Windows (moins les guillemets):

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

un extrait de ceci: utilisez la prudence lors de l'écriture de code qui altère le fait de diriger/suivre des espaces blancs à partir d'une chaîne de noms de fichiers.

18
répondu Jon Schneider 2008-09-19 13:11:27

Microsoft Windows: noyau de Windows interdit l'utilisation de caractères dans la plage de 1 à 31 (c'est à dire, 0x01-0x1F) et des caractères " * : < > ? \ / . Bien que NTFS permette à chaque composant de chemin (répertoire ou nom de fichier) d'avoir une longueur de 255 caractères et des chemins allant jusqu'à 32767 Caractères, Le noyau Windows ne supporte que des chemins allant jusqu'à 259 caractères. En outre, Windows interdit l'utilisation des noms de périphérique MS-DOS AUX, CLOCK$, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, CON, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9, NUL et PRN, ainsi que ces noms avec n'importe quelle extension (par exemple, AUX.txt), sauf lorsqu'on utilise de longs chemins UNC(ex. \.*C:\nul.txt ou \?*D:\aux\con). (En fait, horloge$ peut être utilisé si une extension est fournie.) Ces restrictions ne s'appliquent qu'à Windows-Linux, par exemple, permet l'utilisation de" *:<>? \ / même en NTFS.

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

8
répondu Martin Faartoft 2008-09-15 13:25:43

Plutôt que d'inclure explicitement tous les caractères possibles, vous pourriez faire une regex pour vérifier la présence des caractères illégaux, et signaler une erreur alors. Idéalement, votre application devrait nommer les fichiers exactement comme l'utilisateur le souhaite, et ne crier faute si elle tombe à travers une erreur.

8
répondu ConroyP 2015-06-16 03:20:54

Simplifier la Eugène Katz réponse:

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

ou

bool IsFileNameCorrect(string fileName){
    return fileName.All(f=>!Path.GetInvalidFileNameChars().Contains(f))
}
7
répondu tmt 2018-05-24 10:55:07

j'utilise ceci pour me débarrasser des caractères invalides dans les noms de fichiers sans lancer d'exceptions:

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
répondu JoelFan 2013-02-25 17:24:28

Aussi CON, PRN, AUX, NUL, COM# et quelques autres ne sont jamais juridique des noms de fichiers dans un répertoire avec n'importe quelle extension.

5
répondu 2008-09-15 13:24:18

la question Est de savoir si vous essayez de déterminer si un nom de chemin est un chemin windows légal, ou si c'est légal sur le système où le code est en cours d'exécution. ? Je pense que ce dernier est plus important, donc personnellement, je décomposerais probablement le chemin complet et essaierais d'utiliser _mkdir pour créer le répertoire dans lequel le fichier appartient, puis essaierais de créer le fichier.

de cette façon, vous savez non seulement si le chemin ne contient que des caractères windows valides, mais si il représente un chemin qui peut être écrit par ce processus.

5
répondu kfh 2008-09-15 13:42:27

Pour compléter les autres réponses, voici quelques autres cas de bord que vous pourriez envisager.

4
répondu Joe 2012-01-19 18:52:57

à Partir de MSDN , voici une liste de caractères qui ne sont pas permis:

utilisez presque n'importe quel caractère dans la page de code courante pour un nom, y compris les caractères Unicode et les caractères dans le jeu de caractères étendu (128-255), sauf pour ce qui suit:

  • Les caractères réservés suivants ne sont pas autorisés: < > :"/\/? *
  • les caractères dont le nombre entier est compris entre zéro et 31 ne sont pas autorisés.
  • Tout autre caractère que le système de fichiers cible ne permet pas.
3
répondu Mark Biek 2008-09-15 13:20:54

aussi le système de fichiers de destination est important.

sous NTFS, certains fichiers ne peuvent pas être créés dans des répertoires spécifiques. E. G. $Boot in root

2
répondu Dominik Weber 2010-08-23 20:19:23

c'est une question déjà résolue, mais juste pour le bien des "autres options", voici une non idéale:

(non idéal parce que l'utilisation D'Exceptions comme contrôle du débit est une" mauvaise chose", en général)

public static bool IsLegalFilename(string name)
{
    try 
    {
        var fileInfo = new FileInfo(name);
        return true;
    }
    catch
    {
        return false;
    }
}
2
répondu JerKimball 2012-12-31 19:37:44

les expressions régulières sont exagérées pour cette situation. Vous pouvez utiliser la méthode String.IndexOfAny() en combinaison avec Path.GetInvalidPathChars() et Path.GetInvalidFileNameChars() .

notez aussi que les deux méthodes Path.GetInvalidXXX() clonent un réseau interne et renvoient le clone. Donc si vous allez faire cela souvent (des milliers et des milliers de fois) vous pouvez mettre en cache une copie du tableau de caractères non valide pour la réutilisation.

2
répondu nhahtdh 2013-01-30 16:08:40

beaucoup de ces réponses ne fonctionneront pas si le nom du fichier est trop long et s'exécute sur un environnement pré Windows 10. De même, pensez à ce que vous voulez faire avec les périodes - permettre de mener ou de suivre est techniquement VALIDE, mais peut créer des problèmes si vous ne voulez pas que le fichier soit difficile à voir ou supprimer respectivement.

c'est un attribut de validation que j'ai créé pour vérifier un nom de fichier valide.

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)
    {
        ///q/in-c-check-that-filename-is-possibly-valid-not-that-it-exists-duplicate-29778/",";
    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;
}

et les essais

[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
répondu Brent 2017-03-14 07:35:55

si vous essayez seulement de vérifier si une chaîne de caractères contenant votre nom/chemin de fichier a des caractères invalides, la méthode la plus rapide que j'ai trouvée est d'utiliser Split() pour séparer le nom de fichier en un tableau de parties où il y a un caractère invalide. Si le résultat est seulement un tableau de 1, Il n'y a pas de caractères invalides. :- )

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;

j'ai essayé d'exécuter ceci et d'autres méthodes mentionnées ci-dessus sur un nom de fichier/chemin 1.000.000 de fois dans LinqPad.

en utilisant Split() est seulement ~850ms.

utilisant Regex("[" + Regex.Escape(new string(System.IO.Path.GetInvalidPathChars())) + "]") est d'environ 6 secondes.

les expressions régulières plus compliquées sont bien pires, comme le font certaines des autres options, comme l'utilisation des diverses méthodes sur la classe Path pour obtenir le nom de fichier et laisser leur validation interne faire le travail (très probablement en raison de la gestion des exceptions).

accordé il n'est pas très souvent vous avez besoin de valider 1 million noms de fichiers, donc une seule itération est très bien pour la plupart de ces méthodes de toute façon. Mais c'est quand même assez efficace si vous cherchez seulement des personnages invalides.

1
répondu Nick Albrecht 2017-08-25 18:45:41

Ma tentative:

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 "";
  }
}

ce n'est pas parfait parce que Path.GetInvalidPathChars ne renvoie pas l'ensemble complet des caractères invalides dans les noms de fichiers et de répertoires et bien sûr il y a beaucoup plus de subtilités.

donc j'utilise cette méthode comme un complément:

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

essaie de créer le fichier et retourne false s'il y a une exception. Bien sûr, j'ai besoin de créer le fichier, mais je pense que c'est le plus sûr moyen de le faire. Veuillez également noter que je ne supprime pas les répertoires qui ont été créés.

vous pouvez également utiliser la première méthode pour faire la validation de base, puis gérer soigneusement les exceptions lorsque le chemin est utilisé.

1
répondu Maxence 2017-10-04 16:06:41

les noms de fichiers Windows sont assez peu restrictifs, il se peut donc qu'il ne soit même pas que beaucoup d'un problème. Les caractères qui sont rejetés par Windows sont:

\ / : * ? " < > |

vous pouvez facilement écrire une expression pour vérifier si ces caractères sont présents. Une meilleure solution serait cependant d'essayer de nommer les fichiers comme le veut l'utilisateur, et de les alerter quand un nom de fichier ne colle pas.

0
répondu Justin Poliey 2008-09-15 13:23:55

je suggère juste d'utiliser le chemin.Getfullpath ()

string tagetFileFullNameToBeChecked;
try
{
  Path.GetFullPath(tagetFileFullNameToBeChecked)
}
catch(AugumentException ex)
{
  // invalid chars found
}
0
répondu Tony Sun 2017-01-10 07:57:58

j'ai eu cette idée de quelqu'un. - je ne sais pas qui. Laissez L'OS faire le gros du travail.

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
répondu KenR 2017-10-02 00:30:04

Cette case

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

filtre les noms avec des caractères invalides ( <>:"/\|?* et ASCII 0-31), ainsi que les dispositifs de DOS réservés ( CON , NUL , COMx ). Il permet les espaces de tête et les noms de tous les points, compatible avec Path.GetFullPath . (La création de fichier avec des espaces principaux réussit sur mon système).


utilisé.NET Framework 4.7.1, testé sur Windows 7.

0
répondu Vlad 2018-03-15 13:41:46