¿Cómo verifico si una cadena dada es un nombre de archivo legal / válido en Windows?

165

Quiero incluir una funcionalidad de cambio de nombre de archivo por lotes en mi aplicación. Un usuario puede escribir un patrón de nombre de archivo de destino y (después de reemplazar algunos comodines en el patrón) necesito verificar si será un nombre de archivo legal en Windows. Intenté usar expresiones regulares como, [a-zA-Z0-9_]+pero no incluye muchos caracteres nacionales específicos de varios idiomas (por ejemplo, diéresis, etc.). ¿Cuál es la mejor manera de hacer tal verificación?

tomash
fuente
Sugiero utilizar un Regex compilado estático si va a utilizar cualquiera de las respuestas con Regex ..
AMissico

Respuestas:

100

Puede obtener una lista de caracteres no válidos de Path.GetInvalidPathCharsy GetInvalidFileNameChars.

UPD: Vea la sugerencia de Steve Cooper sobre cómo usarlos en una expresión regular.

UPD2: Tenga en cuenta que de acuerdo con la sección Comentarios en MSDN "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". La respuesta proporcionada por sixlettervaliables entra en más detalles.

Eugene Katz
fuente
11
Esto no responde la pregunta; hay muchas cadenas que consisten solo en caracteres válidos (por ejemplo, "....", "CON", cadenas de cientos de caracteres) que no son nombres de archivo válidos.
Dour High Arch
31
¿Alguien más está decepcionado de que MS no proporcione la función / API de nivel de sistema para esta capacidad en lugar de que cada desarrollador tenga que cocinar su propia solución? Preguntándome si hay una muy buena razón para esto o simplemente una supervisión por parte de la EM.
Thomas Nguyen
@High Arch: vea la respuesta a la pregunta "En C # verifique que el nombre de archivo sea posiblemente válido (no es que exista)" (Aunque algunos tipos inteligentes cerraron esa pregunta a favor de esta ...)
mmmmmmmm
129

Desde "Nombrar un archivo o directorio" de MSDN, estas son las convenciones generales sobre qué es un nombre de archivo legal en Windows:

Puede usar cualquier carácter en la página de códigos actual (Unicode / ANSI superior a 127), excepto:

  • < > : " / \ | ? *
  • Caracteres cuyas representaciones enteras son 0-31 (menos que el espacio ASCII)
  • Cualquier otro carácter que el sistema de archivos de destino no permita (por ejemplo, períodos o espacios finales)
  • Cualquiera de los nombres de DOS: CON, PRN, AUX, NUL, COM0, COM1, COM2, COM3, COM4, ​​COM5, COM6, COM7, COM8, COM9, LPT0, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9 (y evitar AUX.txt, etc.)
  • El nombre del archivo es todos los puntos

Algunas cosas opcionales para verificar:

  • Las rutas de archivo (incluido el nombre del archivo) no pueden tener más de 260 caracteres (que no usan el \?\prefijo)
  • Las rutas de archivo Unicode (incluido el nombre del archivo) con más de 32,000 caracteres cuando se usa \?\(tenga en cuenta que el prefijo puede expandir los componentes del directorio y hacer que desborde el límite de 32,000)
usuario7116
fuente
8
+1 por incluir nombres de archivos reservados: se omitieron en respuestas anteriores.
SqlRyan
2
"AUX" es un nombre de archivo perfectamente utilizable si utiliza la sintaxis "\\? \". Por supuesto, los programas que no utilizan la sintaxis que tienen problemas reales tratar con ella ... (Probado en XP)
user9876
9
La expresión regular correcta para todas estas condiciones mencionadas anteriormente es la siguiente:Regex unspupportedRegex = new Regex("(^(PRN|AUX|NUL|CON|COM[1-9]|LPT[1-9]|(\\.+)$)(\\..*)?$)|(([\\x00-\\x1f\\\\?*:\";|/<>])+)|(([\\. ]+)", RegexOptions.IgnoreCase);
quépor qué
44
@whywhywhy Creo que tienes un soporte de apertura adicional en ese Regex. "(^ (PRN | AUX | NUL | CON | COM [1-9] | LPT [1-9] | (\\. +) $) (\\ .. *)? $) | (([\\ x00 - \\ x1f \\\\? *: \ "; ‌ | / <>]) +) | ([\\.] +)" funcionó para mí.
Wilky
44
Leí el mismo artículo mencionado en esta respuesta y descubrí a través de la experimentación que COM0 y LPT0 tampoco están permitidos. @dlf éste funciona con nombres de archivo que comienzan con '.':^(?!^(?:PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d)(?:\..+)?$)(?:\.*?(?!\.))[^\x00-\x1f\\?*:\";|\/<>]+(?<![\s.])$
mjohnsonengr
67

Para .Net Frameworks anteriores a 3.5, esto debería funcionar:

La coincidencia de expresiones regulares debería ayudarlo. Aquí hay un fragmento que usa 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;
}

Para .Net Frameworks después de 3.0, esto debería funcionar:

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

La coincidencia de expresiones regulares debería ayudarlo. Aquí hay un fragmento que usa 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;
}

Una vez que sepa eso, también debe verificar diferentes formatos, por ejemplo, c:\my\drivey\\server\share\dir\file.ext

Steve Cooper
fuente
¿No prueba esto solo la ruta, no el nombre del archivo?
Eugene Katz
30
string strTheseAreInvalidFileNameChars = nueva cadena (System.IO.Path.GetInvalidFileNameChars ()); Regex regFixFileName = new Regex ("[" + Regex.Escape (strTheseAreInvalidFileNameChars) + "]");
rao
2
Un poco de investigación de la gente haría maravillas. He actualizado la publicación para reflejar los cambios.
Erik Philips
1
La segunda pieza de código no se compila. "No se puede convertir de char [] a cadena
Paul Hunt
1
@AshkanMobayenKhiabani: InvalidPathChars está obsoleto pero GetInvalidPathChars no.
IvanH
25

Intenta usarlo y atrapa el error. El conjunto permitido puede cambiar entre sistemas de archivos o entre diferentes versiones de Windows. En otras palabras, si quieres saber si a Windows le gusta el nombre, dale el nombre y deja que te lo diga.


fuente
1
Este parece ser el único que prueba contra todas las restricciones. ¿Por qué se eligen las otras respuestas sobre esto?
brecha
55
@gap porque no siempre funciona. Por ejemplo, intentar acceder a CON a menudo tendrá éxito, aunque no sea un archivo real.
Antimonio
44
Sin embargo, siempre es mejor evitar la sobrecarga de memoria de lanzar una Excepción, siempre que sea posible.
Owen Blacker
2
Además, es posible que no tenga permisos para acceder a él; por ejemplo, para probarlo escribiendo, incluso si puede leerlo si existe o existirá.
CodeLurker
23

Esta clase limpia nombres de archivo y rutas; úsalo como

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

Aquí está el código;

/// <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
fuente
1
su respuesta podría encajar mejor aquí: stackoverflow.com/questions/146134/…
nawfal
22

Esto es lo que uso:

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

El primer patrón crea una expresión regular que contiene los nombres y caracteres de archivo no válidos / ilegales solo para plataformas Windows. El segundo hace lo mismo pero asegura que el nombre sea legal para cualquier plataforma.

Scott Dorman
fuente
44
sPattern regex no permite archivos iniciados con caracteres de punto. Pero MSDN dice "es aceptable especificar un punto como primer carácter de un nombre. Por ejemplo," .temp "". Quitaría "\ .. *" para hacer que .gitignore nombre de archivo correcto :)
yar_shukan
(Incrementé esto de manera incremental y eliminé los comentarios anteriores que dejé) Este es mejor que la expresión regular de la respuesta porque permite ".gitignore", "..asdf", no permite '<' y '>' o el yen firmar, y no permite espacio o punto al final (que no permite nombres que consisten solo en puntos):@"^(?!(?:PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d)(?:\..+)?$)[^\x00-\x1F\xA5\\?*:\"";|\/<>]+(?<![\s.])$"
mjohnsonengr
esto falla para todos los archivos que probé. ejecutarlo para C: \ Windows \ System32 \ msxml6.dll informa que es falso.
magicandre1981
@ magicandre1981 Debe darle solo el nombre del archivo, no la ruta completa.
Scott Dorman
ok, pero necesito verificar si la ruta completa es válida. Utilicé ahora una solución diferente.
magicandre1981
18

Un caso de esquina para tener en cuenta, que me sorprendió cuando lo descubrí por primera vez: ¡Windows permite caracteres de espacio iniciales en los nombres de archivos! Por ejemplo, los siguientes son todos los nombres de archivo legales y distintos en Windows (menos las comillas):

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

Una conclusión de esto: tenga cuidado al escribir código que recorte los espacios en blanco iniciales / finales de una cadena de nombre de archivo.

Jon Schneider
fuente
10

Simplificando la respuesta de Eugene Katz:

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

O

bool IsFileNameCorrect(string fileName){
    return fileName.All(f=>!Path.GetInvalidFileNameChars().Contains(f))
}
tmt
fuente
¿Quiso decir: "return! FileName.Any (f => Path.GetInvalidFileNameChars (). Contiene (f));" ?
Jack Griffin
@JackGriffin ¡Por supuesto! Gracias por su atención
tmt
Si bien este código es muy agradable de leer, debemos tener en cuenta los aspectos internos lamentables Path.GetInvalidFileNameChars. Eche un vistazo aquí: referencesource.microsoft.com/#mscorlib/system/io/path.cs,289 : para cada carácter de su fileName, se crea un clon de la matriz.
Piotr Zierhoffer
"DD: \\\\\ AAA ..... AAAA". No es válido, pero para su código, lo es.
Ciccio Pasticcio
8

Microsoft Windows: el kernel de Windows prohíbe el uso de caracteres en el rango 1-31 (es decir, 0x01-0x1F) y los caracteres "*: <>? \ |. Aunque NTFS permite que cada componente de ruta (directorio o nombre de archivo) tenga 255 caracteres de longitud y rutas de hasta 32767 caracteres de longitud, el kernel de Windows solo admite rutas de hasta 259 caracteres de longitud. Además, Windows prohíbe el uso de los nombres de dispositivo MS-DOS AUX, CLOCK $, COM1, COM2, COM3, COM4, ​​COM5, COM6, COM7, COM8, COM9, CON, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9, NUL y PRN, así como estos nombres con cualquier extensión (por ejemplo, AUX.txt), excepto cuando se usa Rutas UNC largas (por ejemplo, \. \ C: \ nul.txt o \? \ D: \ aux \ con). (De hecho, CLOCK $ puede usarse si se proporciona una extensión). Estas restricciones solo se aplican a Windows: Linux, por ejemplo, permite el uso de "*: <>? \ | incluso en NTFS.

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

Martin Faartoft
fuente
1
Puedo crear un archivo llamado "CLOCK $" muy bien. Windows 7.
rory.ap
7

En lugar de incluir explícitamente todos los caracteres posibles, puede hacer una expresión regular para verificar la presencia de caracteres ilegales y luego informar un error. Idealmente, su aplicación debería nombrar los archivos exactamente como lo desee el usuario, y solo llorar si encuentra un error.

ConroyP
fuente
6

La pregunta es si está tratando de determinar si un nombre de ruta es una ruta legal de Windows, o si es legal en el sistema donde se está ejecutando el código.? Creo que esto último es más importante, así que personalmente, probablemente descomponga la ruta completa e intente usar _mkdir para crear el directorio al que pertenece el archivo, luego intente crear el archivo.

De esta manera, usted sabe no solo si la ruta contiene solo caracteres válidos de Windows, sino si realmente representa una ruta que puede ser escrita por este proceso.

kfh
fuente
6

Utilizo esto para deshacerme de caracteres no válidos en los nombres de archivo sin lanzar excepciones:

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
fuente
5

Además, CON, PRN, AUX, NUL, COM # y algunos otros nunca son nombres de archivo legales en ningún directorio con ninguna extensión.


fuente
1
Esto es solo la mitad de la verdad. Puede crear archivos con estos nombres si llama a la versión Unicode de CreateFile (prefijando el nombre del archivo con "\\? \").
Werner Henze
Esta declaración está incompleta y pierde LPT #
Thomas Weller
4

Para complementar las otras respuestas, aquí hay un par de casos adicionales adicionales que es posible que desee considerar.

Joe
fuente
3

Desde MSDN , aquí hay una lista de caracteres que no están permitidos:

Use casi cualquier carácter en la página de códigos actual para un nombre, incluidos los caracteres Unicode y los caracteres en el conjunto de caracteres extendido (128–255), excepto lo siguiente:

  • Los siguientes caracteres reservados no están permitidos: <>: "/ \ |? *
  • Los caracteres cuyas representaciones enteras están en el rango de cero a 31 no están permitidos.
  • Cualquier otro carácter que el sistema de archivos de destino no permita.
Mark Biek
fuente
2

También el sistema de archivos de destino es importante.

Bajo NTFS, algunos archivos no se pueden crear en directorios específicos. EG $ Arranque en la raíz

Dominik Weber
fuente
2
¿Seguramente eso no se debe a una regla de nomenclatura NTFS, sino simplemente porque $Bootya existe un archivo llamado en el directorio?
Christian Hayter
2

Esta es una pregunta ya respondida, pero solo por "Otras opciones", aquí hay una no ideal:

(no es ideal porque usar Excepciones como control de flujo es una "mala cosa", generalmente)

public static bool IsLegalFilename(string name)
{
    try 
    {
        var fileInfo = new FileInfo(name);
        return true;
    }
    catch
    {
        return false;
    }
}
JerKimball
fuente
Su ejemplo no funcionó para un archivo CON (C: \ temp \ CON).
tcbrazil
¿Pero no es 'C: \ temp \ CON' un nombre de archivo válido? ¿Por qué no lo sería?
Mark A. Donohoe
@MarqueIV: no, no es válido. Lea todas las respuestas y comentarios anteriores, o pruébelo usted mismo y vea.
rory.ap
@Jer, "/ example" no es legal, pero su método regresa true.
rory.ap
Aaaah ... me perdí la parte 'CON'. El nombre en sí es válido desde un punto de vista de cadena (que es a lo que me refería), pero ahora veo que CON es un nombre reservado, por lo que no es válido desde el punto de vista de Windows. Culpa mía.
Mark A. Donohoe
2

Las expresiones regulares son excesivas para esta situación. Puede usar el String.IndexOfAny()método en combinación con Path.GetInvalidPathChars()y Path.GetInvalidFileNameChars().

También tenga en cuenta que ambos Path.GetInvalidXXX()métodos clonan una matriz interna y devuelven el clon. Entonces, si va a hacer esto mucho (miles y miles de veces), puede almacenar en caché una copia de la matriz de caracteres no válidos para su reutilización.

nhahtdh
fuente
2

Si solo está tratando de verificar si una cadena que contiene su nombre / ruta de archivo tiene caracteres no válidos, el método más rápido que he encontrado es usar Split()para dividir el nombre del archivo en una matriz de partes donde haya un carácter no válido. Si el resultado es solo una matriz de 1, no hay caracteres no válidos. :-)

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;

Intenté ejecutar este y otros métodos mencionados anteriormente en un nombre de archivo / ruta 1,000,000 de veces en LinqPad.

El uso Split()es solo ~ 850 ms.

El uso Regex("[" + Regex.Escape(new string(System.IO.Path.GetInvalidPathChars())) + "]")es de alrededor de 6 segundos.

Las expresiones regulares más complicadas son MUCHO peores, al igual que algunas de las otras opciones, como usar los diversos métodos en la Pathclase para obtener el nombre del archivo y dejar que su validación interna haga el trabajo (muy probablemente debido a la sobrecarga del manejo de excepciones).

De acuerdo, no es muy frecuente que necesite validar 1 millón de nombres de archivo, por lo que una iteración única está bien para la mayoría de estos métodos de todos modos. Pero sigue siendo bastante eficiente y efectivo si solo está buscando caracteres no válidos.

Nick Albrecht
fuente
1

Muchas de estas respuestas no funcionarán si el nombre de archivo es demasiado largo y se ejecuta en un entorno anterior a Windows 10. Del mismo modo, piense qué desea hacer con los períodos: permitir que el inicio o el final sea técnicamente válido, pero puede crear problemas si no desea que el archivo sea difícil de ver o eliminar, respectivamente.

Este es un atributo de validación que creé para buscar un nombre de archivo válido.

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

y las pruebas

[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
fuente
1

Mi intento:

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

Esto no es perfecto porque Path.GetInvalidPathCharsno devuelve el conjunto completo de caracteres que no son válidos en los nombres de archivos y directorios y, por supuesto, hay muchas más sutilezas.

Entonces uso este método como complemento:

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

Intenta crear el archivo y devolver falso si hay una excepción. Por supuesto, necesito crear el archivo, pero creo que es la forma más segura de hacerlo. Tenga en cuenta también que no estoy eliminando directorios que se han creado.

También puede usar el primer método para hacer una validación básica y luego manejar cuidadosamente las excepciones cuando se usa la ruta.

Maxence
fuente
0

Sugiero simplemente usar Path.GetFullPath ()

string tagetFileFullNameToBeChecked;
try
{
  Path.GetFullPath(tagetFileFullNameToBeChecked)
}
catch(AugumentException ex)
{
  // invalid chars found
}
Tony Sun
fuente
Agregue alguna explicación con la respuesta sobre cómo esta respuesta ayuda a OP a solucionar el problema actual
ρяσѕρєя K
Vea el documento en el MSDN para AugumentExcpetion, se lee: la ruta es una cadena de longitud cero, contiene solo espacios en blanco o contiene uno o más de los caracteres no válidos definidos en GetInvalidPathChars. -o bien - El sistema no pudo recuperar la ruta absoluta.
Tony Sun
En teoría (según los documentos) esto debería funcionar, aunque el problema es, al menos en .NET Core 3.1, no lo hace.
Michel Jansson
0

Tengo esta idea de alguien. No sé quién. Deje que el sistema operativo haga el trabajo pesado.

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
fuente
0

Este cheque

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

filtra los nombres con caracteres no válidos ( <>:"/\|?*y ASCII 0-31), así como dispositivos de DOS (reservados CON, NUL, COMx). Permite espacios iniciales y nombres de todos los puntos, de acuerdo con Path.GetFullPath. (La creación de archivos con espacios iniciales tiene éxito en mi sistema).


Usé .NET Framework 4.7.1, probado en Windows 7.

Vlad
fuente
0

Un revestimiento para verificar caracteres ilegales en la cadena:

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

En mi opinión, la única respuesta adecuada a esta pregunta es intentar usar la ruta y dejar que el sistema operativo y el sistema de archivos la validen. De lo contrario, simplemente está reimplementando (y probablemente de manera deficiente) todas las reglas de validación que el sistema operativo y el sistema de archivos ya usan y si esas reglas se cambian en el futuro, tendrá que cambiar su código para que coincida.

Igor Levicki
fuente
-1

Nombres de archivos de Windows son bastante restrictivo, así que realmente no podría ser incluso que gran parte de un problema. Los caracteres que Windows no permite son:

\ / : * ? " < > |

Podría escribir fácilmente una expresión para verificar si esos caracteres están presentes. Sin embargo, una mejor solución sería tratar de nombrar los archivos como el usuario quiera y alertarlos cuando un nombre de archivo no se pegue.

Justin Poliey
fuente
También los caracteres <= 31 están prohibidos.
Antimonio