Comment supprimer les caractères illégaux du chemin et des noms de fichiers?

456

J'ai besoin d'un moyen robuste et simple pour supprimer le chemin d'accès illégal et les caractères de fichier d'une chaîne simple. J'ai utilisé le code ci-dessous mais il ne semble rien faire, que me manque-t-il?

using System;
using System.IO;

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

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

            Console.WriteLine(illegal);
            Console.ReadLine();
        }
    }
}
Gary Willoughby
la source
1
Trim supprime les caractères du début et de la fin des chaînes. Cependant, vous devriez probablement vous demander pourquoi les données ne sont pas valides, et plutôt que d'essayer de désinfecter / corriger les données, rejetez les données.
user7116
8
Les noms de style Unix ne sont pas valides sous Windows et je ne veux pas traiter les noms abrégés 8.3.
Gary Willoughby,
GetInvalidFileNameChars()supprimera des choses comme: \ etc des chemins de dossier.
CAD bloke
1
Path.GetInvalidPathChars()ne semble pas se déshabiller *ou?
CAD bloke
19
J'ai testé cinq réponses à cette question (boucle chronométrée de 100 000) et la méthode suivante est la plus rapide. L'expression régulière a pris la 2e place et était 25% plus lente: chaîne publique GetSafeFilename (chaîne nom de fichier) {chaîne de retour.Join ("_", nom de fichier.Split (Path.GetInvalidFileNameChars ())); }
Brain2000

Réponses:

494

Essayez plutôt quelque chose comme ça;

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

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

Mais je dois être d'accord avec les commentaires, j'essaierais probablement de traiter la source des chemins illégaux, plutôt que d'essayer de transformer un chemin illégal en un chemin légitime mais probablement involontaire.

Edit: Ou une solution potentiellement «meilleure», en utilisant Regex.

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

Pourtant, la question mérite d'être posée, pourquoi vous faites cela en premier lieu.

Matthew Scharley
la source
40
Il n'est pas nécessaire de joindre les deux listes ensemble. La liste de caractères de nom de fichier illégal contient la liste de caractères de chemin illégal et en a quelques autres. Voici les listes des deux listes castées en int: 34,60,62,124,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,58,42,63,92,47 34,60,62,124,0,1,2 , 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27 , 28,29,30,31
Sarel Botha
9
@sjbotha cela peut être vrai sur Windows et la mise en œuvre de Microsoft .NET Je ne suis pas prêt à faire la même supposition pour disons que Linux fonctionne en mono.
Matthew Scharley
7
Concernant la première solution. Un StringBuilder ne devrait-il pas être plus efficace que les affectations de chaînes?
epignosisx
6
Pour ce que ça vaut, @MatthewScharley, l'implémentation Mono de GetInvalidPathChars () ne renvoie que 0x00 et GetInvalidFileNameChars () ne renvoie que 0x00 et '/' lors de l'exécution sur des plates-formes non Windows. Sous Windows, les listes de caractères non valides sont beaucoup plus longues et GetInvalidPathChars () est entièrement dupliqué dans GetInvalidFileNameChars (). Cela ne changera pas dans un avenir prévisible, vous ne faites donc que doubler le temps d'exécution de cette fonction, car vous craignez que la définition d'un chemin valide ne change bientôt. Ce qui ne sera pas le cas.
Warren Rumak
13
@Charleh cette discussion est tellement inutile ... le code doit toujours être optimisé et il n'y a aucun risque que ce soit incorrect. Un nom de fichier fait également partie du chemin d'accès. Il est donc tout simplement illogique de GetInvalidPathChars()contenir des caractères qui GetInvalidFileNameChars()ne le seraient pas. Vous n'acceptez pas l'exactitude d'une optimisation "prématurée". Vous utilisez simplement un mauvais code.
Stefan Fabian
355

La question d'origine demandait de "supprimer les caractères illégaux":

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

Vous pouvez plutôt les remplacer:

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

Cette réponse était sur un autre fil de Ceres , je l'aime vraiment bien et simple.

Shehab Fawzy
la source
10
Pour répondre précisément à la question du PO, vous devez utiliser "" au lieu de "_", mais votre réponse s'applique probablement à plus d'entre nous dans la pratique. Je pense que le remplacement de personnages illégaux par un autre légal est plus courant.
BH
37
J'ai testé cinq méthodes à partir de cette question (boucle chronométrée de 100 000) et cette méthode est la plus rapide. L'expression régulière a pris la 2e place et était 25% plus lente que cette méthode.
Brain2000
10
Pour répondre au commentaire de @BH, on peut simplement utiliser string.Concat (name.Split (Path.GetInvalidFileNameChars ()))
Michael Sutton
210

J'utilise Linq pour nettoyer les noms de fichiers. Vous pouvez facilement l'étendre pour vérifier également les chemins valides.

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

Mise à jour

Certains commentaires indiquent que cette méthode ne fonctionne pas pour eux, j'ai donc inclus un lien vers un extrait DotNetFiddle afin que vous puissiez valider la méthode.

https://dotnetfiddle.net/nw1SWY

Michael Minton
la source
4
Cela n'a pas fonctionné pour moi. La méthode ne renvoie pas la chaîne propre. Il renvoie le nom de fichier transmis tel quel.
Karan
Ce que @Karan a dit, cela ne fonctionne pas, la chaîne d'origine revient.
Jon
Vous pouvez réellement faire avec LINQ comme ceci si: var invalid = new HashSet<char>(Path.GetInvalidPathChars()); return new string(originalString.Where(s => !invalid.Contains(s)).ToArray()). Les performances ne sont probablement pas excellentes, mais cela n'a probablement pas d'importance.
Casey
2
@Karan ou Jon Quelle entrée envoyez-vous cette fonction? Voir ma modification pour la vérification de cette méthode.
Michael Minton,
3
C'est facile - les gars passaient des chaînes avec des caractères valides. A voté pour la solution d'agrégat cool.
Nickmaovich
89

Vous pouvez supprimer des caractères illégaux en utilisant Linq comme ceci:

var invalidChars = Path.GetInvalidFileNameChars();

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

EDIT
Voici à quoi cela ressemble avec l'édition requise mentionnée dans les commentaires:

var invalidChars = Path.GetInvalidFileNameChars();

string invalidCharsRemoved = new string(stringWithInvalidChars
  .Where(x => !invalidChars.Contains(x))
  .ToArray());
Gregor Slavec
la source
1
J'aime cette façon: vous ne conservez que les caractères autorisés dans la chaîne (qui n'est rien d'autre qu'un tableau de caractères).
Mec Pascalou
6
Je sais que c'est une vieille question, mais c'est une excellente réponse. Cependant, je voulais ajouter qu'en c #, vous ne pouvez pas convertir de char [] en chaîne de manière implicite ou explicite (fou, je sais), vous devrez donc le déposer dans un constructeur de chaîne.
JNYRanger
1
Je n'ai pas confirmé cela, mais je m'attends à ce que Path.GetInvalidPathChars () soit un surensemble de GetInvalidFileNameChars () et couvre à la fois les noms de fichiers et les chemins, donc je l'utiliserais probablement à la place.
angularsen
3
@anjdreas en fait Path.GetInvalidPathChars () semble être un sous-ensemble de Path.GetInvalidFileNameChars (), et non l'inverse. Path.GetInvalidPathChars () ne retournera pas '?', Par exemple.
Rafael Costa
1
C'est une bonne réponse. J'utilise à la fois la liste de noms de fichiers et la liste de chemins de fichiers: ____________________________ chaîne cleanData = nouvelle chaîne (data.Where (x =>! Path.GetInvalidFileNameChars (). Contient (x) &&! Path.GetInvalidPathChars (). Contains (x)). ToArray ());
goamn
27

Ce sont toutes d'excellentes solutions, mais elles dépendent toutes Path.GetInvalidFileNameChars, qui ne sont peut-être pas aussi fiables que vous ne le pensez. Notez la remarque suivante dans la documentation MSDN sur Path.GetInvalidFileNameChars:

Le tableau renvoyé par cette méthode n'est pas garanti pour contenir l'ensemble complet de caractères non valides dans les noms de fichiers et de répertoires. L'ensemble complet de caractères non valides peut varier selon le système de fichiers. Par exemple, sur les plates-formes de bureau Windows, les caractères de chemin d'accès non valides peuvent inclure les caractères ASCII / Unicode 1 à 31, ainsi que les guillemets ("), inférieurs à (<), supérieurs à (>), pipe (|), retour arrière ( \ b), null (\ 0) et tab (\ t).

Ce n'est pas mieux avec la Path.GetInvalidPathCharsméthode. Il contient exactement la même remarque.

René
la source
13
Alors quel est l'intérêt de Path.GetInvalidFileNameChars? Je m'attendrais à ce qu'il retourne exactement les caractères invalides pour le système actuel, en s'appuyant sur .NET pour savoir sur quel système de fichiers je suis en cours d'exécution et en me présentant les caractères invalides appropriés. Si ce n'est pas le cas et qu'elle renvoie simplement des caractères codés en dur, qui ne sont pas fiables en premier lieu, cette méthode doit être supprimée car elle a une valeur nulle.
Jan
1
Je sais que c'est un vieux commentaire mais, @Jan, vous pourriez vouloir écrire sur un autre système de fichiers, c'est peut-être pourquoi il y a un avertissement.
fantastik78
3
@ fantastik78 bon point, mais dans ce cas, je voudrais avoir un argument enum supplémentaire pour spécifier mon FS distant. S'il s'agit d'un effort de maintenance trop important (ce qui est probablement le cas), toute cette méthode est toujours une mauvaise idée, car elle vous donne une mauvaise impression de sécurité.
Jan
1
@Jan Je suis totalement d'accord avec toi, je ne faisais que discuter sur l'avertissement.
fantastik78
Il est intéressant de noter qu'il s'agit d'une sorte de "liste noire" de caractères invalides. Ne serait-il pas préférable de "mettre sur liste blanche" uniquement les caractères valides connus ici?! Cela me rappelle l'idée stupide de "virusscanner" au lieu de mettre sur liste blanche les applications autorisées ....
Bernhard
26

Pour les noms de fichiers:

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

Pour les chemins complets:

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

Notez que si vous avez l'intention de l'utiliser comme fonctionnalité de sécurité, une approche plus robuste consisterait à développer tous les chemins, puis à vérifier que le chemin fourni par l'utilisateur est bien un enfant d'un répertoire auquel l'utilisateur devrait avoir accès.

Lily Finley
la source
18

Pour commencer, Trim supprime uniquement les caractères du début ou de la fin de la chaîne . Deuxièmement, vous devez évaluer si vous voulez vraiment supprimer les caractères offensants, ou échouer rapidement et indiquer à l'utilisateur que son nom de fichier n'est pas valide. Mon choix est le dernier, mais ma réponse devrait au moins vous montrer comment faire les choses dans le bon et le mauvais sens:

Question StackOverflow montrant comment vérifier si une chaîne donnée est un nom de fichier valide . Notez que vous pouvez utiliser l'expression régulière de cette question pour supprimer les caractères avec un remplacement d'expression régulière (si vous en avez vraiment besoin).

user7116
la source
Je suis particulièrement d'accord avec le deuxième conseil.
OregonGhost
4
Je serais normalement d'accord avec le second, mais j'ai un programme qui génère un nom de fichier et qui peut contenir des caractères illégaux dans certaines situations. Étant donné que mon programme génère des noms de fichiers illégaux, je pense qu'il est approprié de supprimer / remplacer ces caractères. (Juste en soulignant un cas d'utilisation valide)
JDB se souvient encore de Monica
16

La meilleure façon de supprimer le caractère illégal de l'entrée utilisateur consiste à remplacer le caractère illégal à l'aide de la classe Regex, à créer une méthode dans le code derrière ou à valider côté client à l'aide du contrôle RegularExpression.

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

OU

<asp:RegularExpressionValidator ID="regxFolderName" 
                                runat="server" 
                                ErrorMessage="Enter folder name with  a-z A-Z0-9_" 
                                ControlToValidate="txtFolderName" 
                                Display="Dynamic" 
                                ValidationExpression="^[a-zA-Z0-9_]*$" 
                                ForeColor="Red">
anomepani
la source
5
À mon humble avis, cette solution est bien meilleure que les autres Au lieu de rechercher tous les caractères invalides, il suffit de définir ceux qui sont valides.
igorushi
15

J'utilise des expressions régulières pour y parvenir. Tout d'abord, je crée dynamiquement l'expression régulière.

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

Ensuite, j'appelle simplement removeInvalidChars.Replace pour faire la recherche et le remplacement. Cela peut évidemment être étendu pour couvrir également les caractères de chemin.

Jeff Yates
la source
Étrange, cela a fonctionné pour moi. Je revérifierai quand j'aurai l'occasion. Pouvez-vous être plus précis et expliquer ce qui ne fonctionne pas exactement pour vous?
Jeff Yates
1
Cela ne fonctionnera pas (du moins correctement) car vous n'échappez pas correctement aux caractères du chemin, et certains d'entre eux ont une signification spéciale. Reportez-vous à ma réponse pour savoir comment procéder.
Matthew Scharley
@Jeff: Votre version est toujours meilleure que celle de Matthew, si vous la modifiez légèrement. Référez-vous à ma réponse sur comment.
Jan
2
J'ajouterais également d'autres modèles de nom de fichier invalides que l'on peut trouver sur MSDN et étendrais votre solution aux expressions new Regex(String.Format("^(CON|PRN|AUX|NUL|CLOCK\$|COM[1-9]|LPT[1-9])(?=\..|$)|(^(\.+|\s+)$)|((\.+|\s+)$)|([{0}])", Regex.Escape(new String(Path.GetInvalidFileNameChars()))), RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.CultureInvariant);
régulières
13

Je préfère absolument l'idée de Jeff Yates. Cela fonctionnera parfaitement, si vous le modifiez légèrement:

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

L'amélioration consiste simplement à échapper à l'expression régulière générée automatiquement.

Jan
la source
11

Voici un extrait de code qui devrait aider pour .NET 3 et supérieur.

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

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

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

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

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

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

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

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

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

La plupart des solutions ci-dessus combinent des caractères illégaux pour le chemin et le nom de fichier qui sont incorrects (même lorsque les deux appels renvoient actuellement le même ensemble de caractères). Je diviserais d'abord le chemin + nom de fichier en chemin et nom de fichier, puis appliquer l'ensemble approprié à l'un d'eux, puis combiner à nouveau les deux.

wvd_vegt

wvd_vegt
la source
+1: Très vrai. Aujourd'hui, en travaillant dans .NET 4.0, la solution regex de la réponse du haut a annulé toutes les barres obliques inverses dans un chemin complet. J'ai donc fait une regex pour le chemin du dir et une regex pour juste le nom du fichier, nettoyée séparément et recombinée
dario_ramos
C'est peut-être vrai, mais cela ne répond pas à la question. Je ne suis pas sûr qu'un vague `` je le ferais comme ça '' soit terriblement utile par rapport à certaines des solutions complètes déjà présentes ici (voir par exemple la réponse de Lilly, ci-dessous)
Ian Grainger
6

Si vous supprimez ou remplacez par un seul caractère les caractères non valides, vous pouvez avoir des collisions:

<abc -> abc
>abc -> abc

Voici une méthode simple pour éviter cela:

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

Le résultat:

 <abc -> [1]abc
 >abc -> [2]abc
Maxence
la source
5

Jetez une exception.

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

J'ai écrit ce monstre pour le plaisir, il vous permet d'aller-retour:

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

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

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

    public static int ParseCharArray(char[] buffer)
    {
        int result = 0;
        foreach (char t in buffer)
        {
            int digit = t - '0';
            if ((digit < 0) || (digit > 9))
            {
                throw new ArgumentException("Input string was not in the correct format");
            }
            result *= 10;
            result += digit;
        }
        return result;
    }
}
Johan Larsson
la source
1
J'aime cela car cela évite d'avoir deux chaînes différentes créant le même chemin résultant.
Kim
3

Je pense qu'il est beaucoup plus facile de valider à l'aide d'une expression régulière et de spécifier quels caractères sont autorisés, au lieu d'essayer de vérifier tous les mauvais caractères. Voir ces liens: http://www.c-sharpcorner.com/UploadFile/prasad_1/RegExpPSD12062005021717AM/RegExpPSD.aspx http://www.windowsdevcenter.com/pub/a/oreilly/windows/news/csharp_0101.html

Faites également une recherche pour les "éditeurs d'expressions régulières", ils aident beaucoup. Il y en a autour qui sortent même le code en c # pour vous.

Sandor Davidhazi
la source
Étant donné que .net est un cadre qui est destiné à permettre aux programmes de s'exécuter sur plusieurs plates-formes (par exemple Linux / Unix ainsi que Windows), je pense que Path.GetInvalidFileNameChars () est le meilleur car il contiendra la connaissance de ce qui est ou n'est pas '' t valide sur le système de fichiers sur lequel votre programme est exécuté. Même si votre programme ne fonctionnera jamais sous Linux (il est peut-être plein de code WPF), il y a toujours la possibilité qu'un nouveau système de fichiers Windows apparaisse à l'avenir et ait des caractères valides / invalides différents. Rouler le vôtre avec regex, c'est réinventer la roue et déplacer un problème de plate-forme dans votre propre code.
Daniel Scott
Je suis d'accord avec vos conseils sur les éditeurs / testeurs de regex en ligne. Je les trouve inestimables (car les regex sont des choses délicates et pleines de subtilité qui peuvent vous faire trébucher facilement, vous donnant une regex qui se comporte d'une manière très inattendue avec des cas de bord). Mon préféré est regex101.com (j'aime la façon dont il décompose le regex et vous montre clairement à quoi il s'attend). J'aime aussi assez debuggex.com car il a une représentation visuelle compacte des groupes de correspondance et des classes de personnages et ainsi de suite.
Daniel Scott
3

Cela semble être O (n) et ne dépense pas trop de mémoire sur les chaînes:

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

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

        return new string(name.Where(c => !invalidFileNameChars.Contains(c)).ToArray());
    }
Alexey F
la source
1
Je ne pense pas que ce soit O (n) lorsque vous utilisez la fonction 'Any'.
II ARROWS
@IIARROWS et quel est selon vous?
Alexey F
Je ne sais pas, ça ne me semblait pas comme ça quand j'ai écrit mon commentaire ... maintenant que j'ai essayé de le calculer, on dirait que tu as raison.
II ARROWS
J'ai choisi celui-ci en raison de votre considération de performance. Merci.
Berend Engelbrecht
3

En parcourant les réponses ici, elles semblent toutes ** impliquer l'utilisation d'un tableau de caractères de caractères de nom de fichier non valides.

Certes, cela peut être une micro-optimisation - mais pour le bénéfice de quiconque cherche à vérifier un grand nombre de valeurs pour être des noms de fichiers valides, il convient de noter que la création d'un hachage de caractères non valides améliorera considérablement les performances.

J'ai été très surpris (choqué) par le passé de la rapidité avec laquelle un hachage (ou un dictionnaire) surpasse l'itération sur une liste. Avec des cordes, c'est un nombre ridiculement bas (environ 5-7 éléments de mémoire). Avec la plupart des autres données simples (références d'objet, nombres, etc.), le croisement magique semble être d'environ 20 éléments.

Il y a 40 caractères non valides dans la "liste" Path.InvalidFileNameChars. A fait une recherche aujourd'hui et il y a un bon point de repère ici sur StackOverflow qui montre que le hachage prendra un peu plus de la moitié du temps d'un tableau / liste pour 40 éléments: https://stackoverflow.com/a/10762995/949129

Voici la classe d'assistance que j'utilise pour nettoyer les chemins. J'oublie maintenant pourquoi j'avais une option de remplacement sophistiquée, mais c'est là comme un joli bonus.

Méthode de bonus supplémentaire "IsValidLocalPath" aussi :)

(** ceux qui n'utilisent pas d'expressions régulières)

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


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

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

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

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


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

Vous pouvez utiliser clairement la méthode.

aemre
la source
2

Nom du fichier ne peut contenir que des caractères de Path.GetInvalidPathChars(), +et des #symboles, et les autres noms de particuliers. Nous avons combiné tous les chèques en une seule classe:

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


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

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

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

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

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

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

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

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

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

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

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

        return ReplaceInvalidFileNameSymbols(value, replacementValue);
    }

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

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

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

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

        return string.Empty;
    }
}

La méthode GetValidFileNameremplace toutes les données incorrectes par _.

Dos
la source
2

Un liner pour nettoyer la chaîne de tous les caractères illégaux pour la dénomination des fichiers Windows:

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

Cela vous donnera envie et évitera les collisions

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

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

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

    }
mcintyre321
la source
0

Je pense que la question n'a pas encore répondu complètement ... Les réponses ne décrivent que le nom de fichier propre OU le chemin ... pas les deux. Voici ma solution:

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

J'ai créé une méthode d'extension qui combine plusieurs suggestions:

  1. Tenir des personnages illégaux dans un ensemble de hachage
  2. Filtrage des caractères sous ascii 127. Puisque Path.GetInvalidFileNameChars n'inclut pas tous les caractères non valides possibles avec les codes ascii de 0 à 255. Voir ici et MSDN
  3. Possibilité de définir le caractère de remplacement

La source:

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

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

        return builder.ToString();
    }
}
schoetbi
la source
0

Voici une fonction qui remplace tous les caractères illégaux d'un nom de fichier par un caractère de remplacement:

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

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

Par exemple, le trait de soulignement peut être utilisé comme caractère de remplacement:

NewFileName = ReplaceIllegalFileChars(FileName, '_');
Hans-Peter Kalb
la source
En plus de la réponse que vous avez fournie, veuillez envisager de fournir une brève explication de la raison et de la manière dont cela résout le problème.
jtate
-7

Ou vous pouvez simplement faire

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