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

j'ai besoin d'un moyen robuste et simple pour supprimer le chemin illégal et les caractères de fichier d'une chaîne simple. J'ai utilisé le code ci-dessous mais il ne semble pas faire quoi que ce soit, ce que je manque?

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();
        }
    }
}
362
demandé sur Peter Mortensen 2008-09-28 19:52:10

25 réponses

Essayer quelque chose comme cela, au lieu;

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 avec la source des chemins illégaux, plutôt que d'essayer de barrer un chemin illégal dans un légitime mais probablement involontaire.

éditer: ou une solution potentiellement 'meilleure', en utilisant Regex's.

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 se pose, Pourquoi faites-vous cela en premier lieu.

426
répondu Matthew Scharley 2010-12-13 23:34:58

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

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, donc j'ai inclus un lien vers un snippet DotNetFiddle afin que vous puissiez valider la méthode.

https://dotnetfiddle.net/nw1SWY

199
répondu Michael Minton 2015-09-24 02:47:17
public string GetSafeFilename(string filename)
{

    return string.Join("_", filename.Split(Path.GetInvalidFileNameChars()));

}

cette réponse était sur un autre fil par Cérès , Je l'aime vraiment soigné et simple.

195
répondu Shehab Fawzy 2017-05-23 12:10:07

vous pouvez supprimer les caractères illégaux en utilisant Linq comme ceci:

var invalidChars = Path.GetInvalidFileNameChars();

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

MODIFIER

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

var invalidChars = Path.GetInvalidFileNameChars();

string invalidCharsRemoved = new string(stringWithInvalidChars
  .Where(x => !invalidChars.Contains(x))
  .ToArray());
83
répondu Gregor Slavec 2016-08-22 11:00:15

ce sont toutes de grandes solutions, mais elles s'appuient toutes sur Path.GetInvalidFileNameChars , qui peut ne pas être aussi fiable que vous le pensez. Remarquez la remarque suivante dans la documentation MSDN sur Path.GetInvalidFileNameChars :

le tableau retourné de cette méthode est ne contient pas garanti de contenir l'ensemble complet des caractères qui sont invalides dans les noms de fichiers et de répertoires. l'ensemble des caractères invalides peut varier selon le système de fichiers. Par exemple, sur les plateformes de bureau Windows, les caractères de chemin invalides peuvent inclure les caractères ASCII/Unicode 1 à 31, ainsi que les guillemets ( " ), moins que ( < ), plus grand que ( > ), pipe ( | ), backspace (\b), null (\0) et tab (\t).

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

25
répondu René 2011-11-16 13:29:43

pour commencer, ne supprime que 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 faire savoir à l'Utilisateur leur nom de fichier est invalide. Mon choix est le dernier, mais ma réponse devrait au moins vous montrer comment faire les choses de la bonne et de la mauvaise manière:

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

18
répondu user7116 2017-05-23 12:26:08

pour les noms de fichier:

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

pour les sentiers complets:

string cleanPath = String.Join("", path.Split(Path.GetInvalidPathChars()));
16
répondu Lily Finley 2014-02-11 02:36:30

j'utilise des expressions régulières pour atteindre cet objectif. D'abord, je construis dynamiquement le regex.

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

alors, j'appelle simplement "removeInvalidChars".Remplacer pour faire de la rechercher et remplacer. Cela peut évidemment être étendu pour couvrir chemin de caractères.

15
répondu Jeff Yates 2012-02-13 15:01:48

je préfère L'idée de Jeff Yates. Il 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 est juste pour échapper à la regex générée automatiquement.

14
répondu Jan 2011-02-15 14:21:46

la meilleure façon de supprimer le caractère illégal de l'entrée utilisateur est de remplacer le caractère illégal en utilisant la classe Regex, créer la méthode en code derrière ou aussi le valider du côté du client en utilisant le contrôle de pression Regularex.

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">
13
répondu anomepani 2016-04-17 23:13:14

voici un extrait de code qui devrait aider pour .NET 3 et plus.

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, "");
    }
}
11
répondu James 2010-10-19 16:33:56

la plupart des solutions ci-dessus combinent les caractères illégaux à la fois pour le chemin et le nom de fichier, ce qui est faux (même si les deux appels renvoient actuellement le même jeu de caractères). Je voudrais d'abord diviser le chemin+nom de fichier en chemin et nom de fichier, puis appliquer le jeu approprié à l'un ou l'autre s'ils et puis combiner les deux à nouveau.

wvd_vegt

8
répondu wvd_vegt 2012-06-19 12:16:01

si vous supprimez ou remplacez par un seul caractère les caractères invalides, 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
6
répondu Maxence 2014-10-01 18:40:46

faites une exception.

if ( fileName.IndexOfAny(Path.GetInvalidFileNameChars()) > -1 )
            {
                throw new ArgumentException();
            }
5
répondu mirezus 2009-03-12 16:14:16

j'ai écrit ce monstre pour le plaisir, il vous permet d'aller en rond:

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;
    }
}
4
répondu Johan Larsson 2013-12-07 13:21:54

je pense qu'il est beaucoup plus facile de valider en utilisant un regex et en spécifiant 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

aussi, faire une recherche pour"rédacteur d'expression régulière" s, ils aident beaucoup. Y sont certains autour de qui même la sortie du code en c# pour vous.

3
répondu Sandor Davidhazi 2008-09-28 16:07:56

Cela semble O(n) et de ne pas passer trop de mémoire sur les cordes:

    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());
    }
2
répondu Alexey F 2015-02-09 21:19:41

en parcourant les réponses ici, ils semblent tous* * impliquer l'utilisation d'un tableau de caractères de noms de fichier invalides.

certes, cela peut être micro-optimisant - mais pour le bénéfice de quiconque pourrait être à la recherche de vérifier un grand nombre de valeurs pour être des noms de fichiers valides, il est intéressant de noter que la construction d'un hashset de caractères invalides apportera des performances nettement meilleures.

j'ai été très surpris (choqué) dans le passé comment rapidement un hashset (ou dictionnaire), surpasse itérer sur une liste. Avec strings, 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'objets, numéros, etc.), le crossover magique semble être d'environ 20 éléments.

il y a 40 caractères invalides dans le chemin.InvalidFileNameChars "list". J'ai fait une recherche aujourd'hui et il y a un bon benchmark ici sur StackOverflow qui montre que le hashset va prendre 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 helper que j'utilise pour assainir les chemins. J'ai oublié pourquoi j'avais l'option de remplacement, mais c'est un joli bonus.

bonus Supplémentaire de la méthode "IsValidLocalPath":)

(**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 ?? '"151900920"';
                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 != '"151900920"')
                    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;
    }
}
2
répondu Daniel Scott 2017-09-08 00:20:26
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 la méthode clairement.

2
répondu aemre 2018-02-22 11:25:19
public static bool IsValidFilename(string testName)
{
    return !new Regex("[" + Regex.Escape(new String(System.IO.Path.GetInvalidFileNameChars())) + "]").IsMatch(testName);
}
1
répondu mbdavis 2013-11-18 13:28:53
Le nom de fichier

ne peut pas contenir les caractères des symboles Path.GetInvalidPathChars() , + et # , ni d'autres noms spécifiques. Nous avons regroupé tous les contrôles 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;
    }
}

Méthode GetValidFileName remplace toutes les données incorrectes _ .

1
répondu Backs 2018-07-25 12:14:08

cela va vouloir que vous voulez, et éviter 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();

    }
0
répondu mcintyre321 2014-09-19 15:04:57

je pense que la question n'a pas déjà répondu pleinement... Les réponses ne décrivent que le nom propre du fichier 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;
}
0
répondu Suplanus 2015-07-07 11:45:42

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

  1. la Tenue de caractères illégaux dans une table de hachage ensemble
  2. filtrant les caractères sous ascii 127. Depuis Le Chemin D'Accès.GetInvalidFileNameChars n'inclut pas tous les caractères invalides possibles avec les codes ascii de 0 à 255. voir ici et MSDN
  3. possibilité de définir le caractère de remplacement

Source:

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

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

        return builder.ToString();
    }
}
0
répondu schoetbi 2018-06-14 07:11:53

Ou vous pouvez simplement faire

[YOUR STRING].Replace('\', ' ').Replace('/', ' ').Replace('"', ' ').Replace('*', ' ').Replace(':', ' ').Replace('?', ' ').Replace('<', ' ').Replace('>', ' ').Replace('|', ' ').Trim();
-6
répondu Danny Fallas 2014-01-15 21:24:52