Comment vérifier si une chaîne donnée est un nom de fichier légal / valide sous Windows?

165

Je souhaite inclure une fonctionnalité de changement de nom de fichier de commandes dans mon application. Un utilisateur peut taper un modèle de nom de fichier de destination et (après avoir remplacé certains 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 des expressions régulières comme [a-zA-Z0-9_]+mais cela n'inclut pas beaucoup de caractères nationaux spécifiques de différentes langues (par exemple, des trémas, etc.). Quelle est la meilleure façon de procéder à une telle vérification?

Tomash
la source
Je suggère d'utiliser un Regex compilé statique si vous prévoyez d'utiliser l'une des réponses avec Regex ..
AMissico

Réponses:

100

Vous pouvez obtenir une liste de caractères non valides à partir de Path.GetInvalidPathCharset GetInvalidFileNameChars.

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

UPD2: Notez que selon la section Remarques dans MSDN "Le tableau renvoyé par cette méthode n'est pas garanti pour contenir le jeu complet de caractères qui ne sont pas valides dans les noms de fichiers et de répertoires." La réponse fournie par sixlettervaliables entre dans plus de détails.

Eugène Katz
la source
11
Cela ne répond pas à la question; il existe de nombreuses chaînes constituées uniquement de caractères valides (par exemple "....", "CON", des chaînes de centaines de caractères) qui ne sont pas des noms de fichiers valides.
Dour High Arch
31
Quelqu'un d'autre est-il déçu que MS ne fournisse pas de fonction / API au niveau du système pour cette capacité au lieu que chaque développeur doive cuisiner sa propre solution? Vous vous demandez s'il y a une très bonne raison à cela ou simplement un oubli de la part de la SP.
Thomas Nguyen
@High Arch: Voir la réponse à la question "En C # vérifier que le nom de fichier est peut - être valide (pas qu'il existe)". (Bien que certains types intelligents
aient résolu
129

À partir de «Nommer un fichier ou un répertoire» de MSDN, voici les conventions générales pour définir ce qu'est un nom de fichier légal sous Windows:

Vous pouvez utiliser n'importe quel caractère de la page de codes actuelle (Unicode / ANSI supérieur à 127), sauf:

  • < > : " / \ | ? *
  • Caractères dont les représentations entières sont comprises entre 0 et 31 (inférieurs à l'espace ASCII)
  • Tout autre caractère que le système de fichiers cible n'autorise pas (par exemple, des points ou des espaces de fin)
  • Tous les noms 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 évitez AUX.txt, etc.)
  • Le nom du fichier est toutes les périodes

Quelques éléments facultatifs à vérifier:

  • Les chemins de fichier (y compris le nom de fichier) ne doivent pas comporter plus de 260 caractères (qui n'utilisent pas le \?\préfixe)
  • Chemins de fichier Unicode (y compris le nom de fichier) avec plus de 32 000 caractères lors de l'utilisation \?\(notez que le préfixe peut développer les composants du répertoire et provoquer le dépassement de la limite de 32 000)
user7116
la source
8
+1 pour inclure les noms de fichiers réservés - ceux-ci ont été manqués dans les réponses précédentes.
SqlRyan
2
"AUX" est un nom de fichier parfaitement utilisable si vous utilisez la syntaxe "\\? \". Bien sûr, les programmes qui n'utilisent pas cette syntaxe ont de réels problèmes pour y faire face ... (Testé sur XP)
user9876
9
L'expression régulière correcte pour toutes ces conditions mentionnées ci-dessus est la suivante:Regex unspupportedRegex = new Regex("(^(PRN|AUX|NUL|CON|COM[1-9]|LPT[1-9]|(\\.+)$)(\\..*)?$)|(([\\x00-\\x1f\\\\?*:\";|/<>])+)|(([\\. ]+)", RegexOptions.IgnoreCase);
whywhywhy
4
@whywhywhy je pense que vous avez une parenthèse d'ouverture supplémentaire dans ce Regex. "(^ (PRN | AUX | NUL | CON | COM [1-9] | LPT [1-9] | (\\. +) $) (\\ .. *)? $) | (([\\ x00 - \\ x1f \\\\? *: \ "; ‌ | / <>]) +) | ([\\.] +)" a fonctionné pour moi.
Wilky
4
J'ai lu le même article mentionné dans cette réponse et j'ai trouvé par expérimentation que COM0 et LPT0 ne sont pas non plus autorisés. @dlf celui-ci fonctionne avec les noms de fichiers commençant par '.':^(?!^(?:PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d)(?:\..+)?$)(?:\.*?(?!\.))[^\x00-\x1f\\?*:\";|\/<>]+(?<![\s.])$
mjohnsonengr
67

Pour les frameworks .Net antérieurs à la version 3.5, cela devrait fonctionner:

La correspondance d'expressions régulières devrait vous aider. Voici un extrait de code utilisant la System.IO.Path.InvalidPathCharsconstante;

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 .Net Frameworks après 3.0, cela devrait fonctionner:

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

La correspondance d'expressions régulières devrait vous aider. Voici un extrait de code utilisant la System.IO.Path.GetInvalidPathChars()constante;

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 cela, vous devriez également vérifier les différents formats, par exemple c:\my\driveet\\server\share\dir\file.ext

Steve Cooper
la source
cela ne teste-t-il pas seulement le chemin, pas le nom de fichier?
Eugene Katz le
30
string strTheseAreInvalidFileNameChars = nouvelle chaîne (System.IO.Path.GetInvalidFileNameChars ()); Regex regFixFileName = new Regex ("[" + Regex.Escape (strTheseAreInvalidFileNameChars) + "]");
rao
2
Un peu de recherche auprès des gens ferait des merveilles. J'ai mis à jour le message pour refléter les changements.
Erik Philips
1
Le deuxième morceau de code ne se compile pas. "Impossible de convertir char [] en chaîne
Paul Hunt
1
@AshkanMobayenKhiabani: InvalidPathChars est obsolète mais pas GetInvalidPathChars.
IvanH
25

Essayez de l'utiliser et piègez l'erreur. L'ensemble autorisé peut changer entre les systèmes de fichiers ou entre les différentes versions de Windows. En d'autres termes, si vous voulez savoir si Windows aime le nom, donnez-lui le nom et laissez-le vous dire.


la source
1
Cela semble être le seul qui teste contre toutes les contraintes. Pourquoi les autres réponses sont-elles choisies à ce sujet?
écart
5
@gap car cela ne fonctionne pas toujours. Par exemple, essayer d'accéder à CON réussira souvent, même s'il ne s'agit pas d'un vrai fichier.
Antimoine
4
Il est toujours préférable d'éviter la surcharge de mémoire liée à la levée d'une exception, dans la mesure du possible.
Owen Blacker
2
En outre, vous ne disposez peut-être pas des autorisations nécessaires pour y accéder; par exemple pour le tester en écrivant, même si vous pouvez le lire s'il existe ou existera.
CodeLurker
23

Cette classe nettoie les noms de fichiers et les chemins; utilisez-le 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();
    }

}
Steve Cooper
la source
1
votre réponse pourrait être mieux adaptée ici: stackoverflow.com/questions/146134/…
nawfal
22

Voici 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 modèle crée une expression régulière contenant les noms de fichiers et les caractères non valides / illégaux pour les plates-formes Windows uniquement. Le second fait de même mais garantit que le nom est légal pour n'importe quelle plateforme.

Scott Dorman
la source
4
sPattern regex n'autorise pas les fichiers commençant par le caractère point. Mais MSDN dit "il est acceptable de spécifier un point comme premier caractère d'un nom. Par exemple," .temp "". Je supprimerais "\ .. *" pour que le nom de fichier .gitignore soit correct :)
yar_shukan
(J'ai progressivement amélioré et supprimé les commentaires précédents que j'ai laissés) Celui-ci est meilleur que l'expression régulière de la réponse car il permet ".gitignore", "..asdf", ne permet pas '<' et '>' ou le yen signe, et n'autorise pas d'espace ou de point à la fin (ce qui interdit les noms composés uniquement de points):@"^(?!(?:PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d)(?:\..+)?$)[^\x00-\x1F\xA5\\?*:\"";|\/<>]+(?<![\s.])$"
mjohnsonengr
cela échoue pour tous les fichiers que j'ai testés. l'exécuter pour C: \ Windows \ System32 \ msxml6.dll signale false.
magicandre1981
@ magicandre1981 Vous devez lui donner uniquement le nom du fichier, pas le chemin complet.
Scott Dorman
ok, mais je dois vérifier si le chemin complet est valide. J'ai utilisé maintenant une solution différente.
magicandre1981
18

Un cas d'angle à garder à l'esprit, ce qui m'a surpris lorsque j'ai découvert cela pour la première fois: Windows autorise les caractères d'espace de début dans les noms de fichiers! Par exemple, les noms de fichiers suivants sont tous légaux et distincts sous Windows (moins les guillemets):

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

Une chose à retenir: soyez prudent lorsque vous écrivez du code qui supprime les espaces de début / de fin d'une chaîne de nom de fichier.

Jon Schneider
la source
10

Simplifier la réponse d'Eugene Katz:

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))
}
tmt
la source
Vouliez-vous dire: "return! FileName.Any (f => Path.GetInvalidFileNameChars (). Contains (f));" ?
Jack Griffin
@JackGriffin Bien sûr! Merci pour votre attention.
tmt
Bien que ce code soit très agréable à lire, nous devons prendre en compte les désolés internes de Path.GetInvalidFileNameChars. Jetez un œil ici: referencesource.microsoft.com/#mscorlib/system/io/path.cs,289 - pour chaque caractère de votre fileName, un clone du tableau est créé.
Piotr Zierhoffer
"DD: \\\\\ AAA ..... AAAA". Non valide, mais pour votre code, ça l'est.
Ciccio Pasticcio le
8

Microsoft Windows: le noyau Windows interdit l'utilisation de caractères compris entre 1 et 31 (c'est-à-dire 0x01-0x1F) et les caractères "*: <>? \ |. Bien que NTFS autorise chaque composant de chemin (répertoire ou nom de fichier) à 255 caractères et jusqu'à environ 32 767 caractères, le noyau Windows ne prend en charge que les chemins jusqu'à 259 caractères. De plus, 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 lors de l'utilisation Chemins UNC longs (ex. \. \ C: \ nul.txt ou \? \ D: \ aux \ con). (En fait, CLOCK $ peut être utilisé si une extension est fournie.) Ces restrictions s'appliquent uniquement à Windows - Linux, par exemple, autorise l'utilisation de "*: <>? \ | même en NTFS.

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

Martin Faartoft
la source
1
Je peux créer un fichier nommé "CLOCK $" très bien. Windows 7.
rory.ap
7

Plutôt que d'inclure explicitement tous les caractères possibles, vous pouvez faire une regex pour vérifier la présence de caractères illégaux et signaler une erreur ensuite. Idéalement, votre application doit nommer les fichiers exactement comme l'utilisateur le souhaite, et ne crier au scandale que si elle tombe sur une erreur.

ConroyP
la source
6

La question est de savoir si vous essayez de déterminer si un nom de chemin est un chemin Windows légal ou s'il est légal sur le système sur lequel le code est exécuté. ? Je pense que ce dernier est plus important, donc personnellement, je décomposerais probablement le chemin complet et essayerais d'utiliser _mkdir pour créer le répertoire auquel appartient le fichier, puis essayer de créer le fichier.

De cette façon, vous savez non seulement si le chemin contient uniquement des caractères Windows valides, mais s'il représente en fait un chemin qui peut être écrit par ce processus.

kfh
la source
6

J'utilise ceci pour me débarrasser des caractères invalides dans les noms de fichiers sans lever 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);
}
JoelFan
la source
5

De plus, CON, PRN, AUX, NUL, COM # et quelques autres ne sont jamais des noms de fichiers légaux dans aucun répertoire avec une extension.


la source
1
Ce n'est que la moitié de la vérité. Vous pouvez créer des fichiers avec ces noms si vous appelez la version unicode de CreateFile (en préfixant le nom de fichier avec "\\? \").
Werner Henze
Cette déclaration est incomplète et manque LPT #
Thomas Weller
4

Pour compléter les autres réponses, voici quelques cas supplémentaires que vous voudrez peut-être envisager.

Joe
la source
3

Depuis MSDN , voici une liste de caractères non autorisés:

Utilisez presque tous les caractères de la page de codes actuelle pour un nom, y compris les caractères Unicode et les caractères du jeu de caractères étendu (128–255), à l'exception des éléments suivants:

  • Les caractères réservés suivants ne sont pas autorisés: <>: "/ \ |? *
  • Les caractères dont les représentations entières sont comprises entre zéro et 31 ne sont pas autorisés.
  • Tout autre caractère non autorisé par le système de fichiers cible.
Mark Biek
la source
2

Le système de fichiers de destination est également important.

Sous NTFS, certains fichiers ne peuvent pas être créés dans des répertoires spécifiques. EG $ Boot en root

Dominik Weber
la source
2
Ce n'est sûrement pas dû à une règle de dénomination NTFS, mais simplement parce qu'un fichier appelé $Bootexiste déjà dans le répertoire?
Christian Hayter
2

C'est une question déjà répondue, mais juste pour le plaisir des "Autres options", en voici une non idéale:

(non idéal car l'utilisation d'exceptions comme contrôle de flux est généralement une mauvaise chose)

public static bool IsLegalFilename(string name)
{
    try 
    {
        var fileInfo = new FileInfo(name);
        return true;
    }
    catch
    {
        return false;
    }
}
JerKimball
la source
Votre exemple n'a pas fonctionné pour un fichier CON (C: \ temp \ CON).
tcbrazil
Mais «C: \ temp \ CON» n'est-il pas un nom de fichier valide? Pourquoi pas?
Mark A. Donohoe
@MarqueIV - non, ce n'est pas valide. Lisez toutes les réponses et commentaires ci-dessus, ou essayez-le vous-même et voyez.
rory.ap
@Jer, "/ example" n'est pas légal, mais votre méthode retourne true.
rory.ap
Aaaah ... J'ai raté la partie «CON». Le nom lui-même est valide du point de vue de la chaîne (ce à quoi je faisais référence), mais je vois maintenant que CON est un nom réservé, ce qui le rend non valide du point de vue de Windows. Ma faute.
Mark A. Donohoe
2

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

Notez également que les deux Path.GetInvalidXXX()méthodes clonent un tableau interne et renvoient le clone. Donc, si vous comptez faire cela beaucoup (des milliers et des milliers de fois), vous pouvez mettre en cache une copie du tableau de caractères invalides pour une réutilisation.

nhahtdh
la source
2

Si vous essayez seulement de vérifier si une chaîne contenant votre nom / chemin de fichier contient des caractères non valides, la méthode la plus rapide que j'ai trouvée consiste Split()à diviser le nom de fichier en un tableau de parties partout où il y a un caractère non valide. Si le résultat n'est qu'un tableau de 1, il n'y a pas de caractères non valides. :-)

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 cette méthode et d'autres méthodes mentionnées ci-dessus sur un nom de fichier / chemin 1 000 000 fois dans LinqPad.

L'utilisation Split()ne dure que ~ 850 ms.

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

Les expressions régulières les plus compliquées sont bien pires, tout comme certaines des autres options, comme l'utilisation des différentes méthodes de la Pathclasse pour obtenir le nom de fichier et laisser leur validation interne faire le travail (probablement en raison de la surcharge de la gestion des exceptions).

Certes, il n'est pas très souvent nécessaire de valider 1 million de noms de fichiers, donc une seule itération convient de toute façon pour la plupart de ces méthodes. Mais c'est toujours assez efficace et efficace si vous ne recherchez que des caractères invalides.

Nick Albrecht
la source
1

beaucoup de ces réponses ne fonctionneront pas si le nom de fichier est trop long et s'exécute dans un environnement antérieur à Windows 10. De même, pensez à ce que vous voulez faire avec les points - autoriser le début ou la fin 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)
    {
        //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;
}

et les 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
la source
1

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 car Path.GetInvalidPathChars ne renvoie pas le jeu complet de caractères invalides dans les noms de fichiers et de répertoires et bien sûr, il y a beaucoup plus de subtilités.

J'utilise donc cette méthode en 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;
  }
}

Il essaie de créer le fichier et renvoie false s'il y a une exception. Bien sûr, je dois créer le fichier, mais je pense que c'est le moyen le plus sûr 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 effectuer une validation de base, puis gérer soigneusement les exceptions lorsque le chemin est utilisé.

Maxence
la source
0

Je suggère simplement d'utiliser le Path.GetFullPath ()

string tagetFileFullNameToBeChecked;
try
{
  Path.GetFullPath(tagetFileFullNameToBeChecked)
}
catch(AugumentException ex)
{
  // invalid chars found
}
Tony Sun
la source
Ajoutez une explication avec une réponse sur la façon dont cette réponse aide OP à résoudre le problème actuel
ρяσѕρєя K
Consultez le document dans le MSDN pour AugumentExcpetion, il lit: chemin est une chaîne de longueur nulle, contient uniquement un espace blanc ou contient un ou plusieurs des caractères non valides définis dans GetInvalidPathChars. -ou- Le système n'a pas pu récupérer le chemin d'accès absolu.
Tony Sun
En théorie (selon la documentation), cela devrait fonctionner, mais le problème est qu'au moins dans .NET Core 3.1, ce n'est pas le cas.
Michel Jansson le
0

J'ai eu cette idée de quelqu'un. - ne sais pas qui. Laissez le système d'exploitation 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;
}
KenR
la source
0

Cette vérification

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

filtre les noms avec les caractères non valides ( <>:"/\|?*et ASCII 0-31), ainsi que des dispositifs DOS réservés ( CON, NUL, COMx). Il permet des espaces de début et des noms de points, cohérents avec Path.GetFullPath. (La création d'un fichier avec des espaces de début réussit sur mon système).


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

Vlad
la source
0

Une doublure pour vérifier les caractères illigaux dans la chaîne:

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

À mon avis, la seule réponse appropriée à cette question est d'essayer d'utiliser le chemin et de laisser le système d'exploitation et le système de fichiers le valider. Sinon, vous ne faites que réimplémenter (et probablement mal) toutes les règles de validation que le système d'exploitation et le système de fichiers utilisent déjà et si ces règles sont modifiées à l'avenir, vous devrez modifier votre code pour les faire correspondre.

Igor Levicki
la source
-1

Windows sont assez les noms de fichiers non restrictive, si vraiment il pourrait même ne pas être que beaucoup d'une question. Les caractères non autorisé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 l'utilisateur le souhaite et de les alerter lorsqu'un nom de fichier ne colle pas.

Justin Poliey
la source
Les caractères <= 31 sont également interdits.
Antimoine