Pregunta

Necesito una forma sólida y sencilla de eliminar rutas ilegales y caracteres de archivo de una cadena simple.He usado el siguiente código pero no parece hacer nada, ¿qué me falta?

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();
        }
    }
}
¿Fue útil?

Solución

Pruebe algo como esto en su lugar;

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

Pero tengo que estar de acuerdo con los comentarios, probablemente intente tratar con la fuente de los caminos ilegales, en lugar de tratar de destrozar un camino ilegal en uno legítimo pero probablemente no intencionado.

Editar: O una solución potencialmente 'mejor', usando 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, "");

Aún así, la pregunta es una pregunta, ¿por qué estás haciendo esto en primer lugar?

Otros consejos

public string GetSafeFilename(string filename)
{

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

}

Esta respuesta fue en otro hilo de Ceres , realmente me gusta ordenada y simple.

Uso Linq para limpiar nombres de archivos. También puede ampliar esto fácilmente para buscar rutas válidas también.

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

Actualizar

Algunos comentarios indican que este método no funciona para ellos, por lo que he incluido un enlace a un fragmento de DotNetFiddle para que pueda validar el método.

https://dotnetfiddle.net/nw1SWY

Puede eliminar caracteres ilegales usando Linq así:

var invalidChars = Path.GetInvalidFileNameChars();

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

EDITAR
Así es como se ve con la edición requerida mencionada en los comentarios:

var invalidChars = Path.GetInvalidFileNameChars();

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

Estas son todas excelentes soluciones, pero todas dependen de Path.GetInvalidFileNameChars, que puede no ser tan confiable como parece. Observe la siguiente observación en la documentación de MSDN en Path.GetInvalidPathChars :

  

No se garantiza que la matriz devuelta por este método contenga el conjunto completo de caracteres que no son válidos en los nombres de archivo y directorio. El conjunto completo de caracteres no válidos puede variar según el sistema de archivos. Por ejemplo, en plataformas de escritorio basadas en Windows, los caracteres de ruta no válidos pueden incluir caracteres ASCII / Unicode del 1 al 31, así como comillas (& Quot;), menores que (& Lt;), mayores que ( gt;), tubería (|), retroceso (\ b), nulo (\ 0) y tabulación (\ t).

No es mejor con el método <=> . Contiene exactamente el mismo comentario.

Para nombres de archivo:

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

Para rutas completas:

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

Tenga en cuenta que si tiene la intención de utilizar esto como una característica de seguridad, un enfoque más robusto sería expandir todas las rutas y luego verificar que la ruta proporcionada por el usuario sea realmente un elemento secundario de un directorio al que el usuario debería tener acceso.

Para empezar, Recortar solo elimina caracteres del principio o final de la cadena . En segundo lugar, debe evaluar si realmente desea eliminar los caracteres ofensivos, o fallar rápidamente y dejar que el usuario sepa que su nombre de archivo no es válido. Mi elección es la última, pero mi respuesta al menos debería mostrarle cómo hacer las cosas de la manera correcta e incorrecta:

Pregunta de StackOverflow que muestra cómo compruebe si una cadena dada es un nombre de archivo válido . Tenga en cuenta que puede usar la expresión regular de esta pregunta para eliminar caracteres con un reemplazo de expresión regular (si realmente necesita hacer esto).

Utilizo expresiones regulares para lograr esto. Primero, construyo dinámicamente la expresión regular.

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

Entonces solo llamo removeInvalidChars.Replace para hacer la búsqueda y reemplazar. Obviamente, esto también se puede extender para cubrir caracteres de ruta.

La mejor manera de eliminar caracteres ilegales de la entrada del usuario es reemplazar los caracteres ilegales usando la clase Regex, crear un método en el código detrás o también validar en el lado del cliente usando el control RegularExpression.

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

OR

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

Prefiero absolutamente la idea de Jeff Yates. Funcionará perfectamente, si lo modifica ligeramente:

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

La mejora es solo para escapar de la expresión regular generada automáticamente.

Aquí hay un fragmento de código que debería ayudar para .NET 3 y superior.

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

La mayoría de las soluciones anteriores combinan caracteres ilegales para la ruta y el nombre de archivo que es incorrecto (incluso cuando ambas llamadas devuelven el mismo conjunto de caracteres). Primero dividiría la ruta + nombre de archivo en ruta y nombre de archivo, luego aplicaría el conjunto apropiado a cualquiera de ellos y luego combinaría los dos nuevamente.

wvd_vegt

Si elimina o reemplaza con un solo carácter los caracteres no válidos, puede tener colisiones:

<abc -> abc
>abc -> abc

Aquí hay un método simple para evitar esto:

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

El resultado:

 <abc -> [1]abc
 >abc -> [2]abc

Lanzar una excepción.

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

Escribí este monstruo por diversión, te permite un viaje de ida y vuelta:

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

Creo que es mucho más fácil validar usando una expresión regular y especificar qué caracteres están permitidos, en lugar de intentar verificar todos los caracteres malos. Ver estos enlaces: http://www.c-sharpcorner.com/UploadFile/prasad_1/ RegExpPSD12062005021717AM / RegExpPSD.aspx http://www.windowsdevcenter.com/pub/a/ oreilly / windows / news / csharp_0101.html

Además, haga una búsqueda de " editor de expresiones regulares " s, ayudan mucho. Hay algunos que incluso generan el código en C # para usted.

Esto parece ser O (n) y no gasta demasiada memoria en cadenas:

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

Al escanear las respuestas aquí, todas ** parecen implicar el uso de una matriz de caracteres de nombre de archivo no válido.

Por supuesto, esto puede ser micro-optimización, pero para el beneficio de cualquiera que esté buscando verificar una gran cantidad de valores para ser nombres de archivo válidos, vale la pena señalar que construir un hashset de caracteres no válidos traerá un rendimiento notablemente mejor .

Me ha sorprendido (sorprendido) en el pasado lo rápido que un hashset (o diccionario) supera el iterar sobre una lista. Con cadenas, es un número ridículamente bajo (aproximadamente 5-7 elementos de la memoria). Con la mayoría de los datos simples (referencias de objetos, números, etc.), el crossover mágico parece ser de alrededor de 20 elementos.

Hay 40 caracteres no válidos en la ruta.InvalidFileNameChars " list " ;. Hice una búsqueda hoy y hay un punto de referencia bastante bueno aquí en StackOverflow que muestra que el hashset tomará un poco más de la mitad del tiempo de una matriz / lista para 40 elementos: https://stackoverflow.com/a/10762995/949129

Aquí está la clase auxiliar que uso para desinfectar rutas. Ahora olvido por qué tenía la opción de reemplazo elegante, pero está ahí como un lindo bono.

Método de bonificación adicional " IsValidLocalPath " también :)

(** aquellos que no usan expresiones regulares)

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

Puede usar el método claramente.

El nombre del archivo no puede contener caracteres de Path.GetInvalidPathChars(), + y # símbolos y otros nombres específicos.Combinamos todos los controles en una clase:

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étodo GetValidFileName reemplaza todos los datos incorrectos para _.

Un revestimiento para limpiar la cadena de caracteres ilegales para los nombres de archivos de 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, "");
public static bool IsValidFilename(string testName)
{
    return !new Regex("[" + Regex.Escape(new String(System.IO.Path.GetInvalidFileNameChars())) + "]").IsMatch(testName);
}

Esto hará lo que quieras y evitar colisiones

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

    }

Creo que la pregunta ya no está respondida por completo ... Las respuestas solo describen el nombre de archivo limpio O la ruta ... no ambas. Aquí está mi solución:

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

Creé un método de extensión que combina varias sugerencias:

  1. Retener caracteres ilegales en un conjunto de hash
  2. Filtrar caracteres debajo de ascii 127. Dado que Path.GetInvalidFileNameChars no incluye todos los caracteres no válidos posibles con códigos ascii de 0 a 255. Ver aquí y MSDN
  3. Posibilidad de definir el carácter de reemplazo

Fuente:

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

O simplemente puedes hacer

[YOUR STRING].Replace('\\', ' ').Replace('/', ' ').Replace('"', ' ').Replace('*', ' ').Replace(':', ' ').Replace('?', ' ').Replace('<', ' ').Replace('>', ' ').Replace('|', ' ').Trim();
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top