¿Hay alguna forma de hacer que la ruta de archivo de cadenas sea segura en c #?
93
Mi programa tomará cadenas arbitrarias de Internet y las usará para nombres de archivos. ¿Existe una forma sencilla de eliminar los caracteres incorrectos de estas cadenas o necesito escribir una función personalizada para esto?
Uf, odio cuando la gente trata de adivinar qué caracteres son válidos. Además de ser completamente no portátil (siempre pensando en Mono), los dos comentarios anteriores perdieron más 25 caracteres no válidos.
'Clean just a filenameDim filename AsString="salmnas dlajhdla kjha;dmas'lkasn"ForEach c In IO.Path.GetInvalidFileNameChars
filename = filename.Replace(c,"")Next'See also IO.Path.GetInvalidPathChars
La versión de C #: foreach (var c en Path.GetInvalidFileNameChars ()) {fileName = fileName.Replace (c, '-'); }
jcollum
8
¿Cómo manejaría esta solución los conflictos de nombres? Parece que más de una cadena puede coincidir con un solo nombre de archivo ("¿Infierno?" E "Infierno *", por ejemplo). Si está bien, solo elimina los caracteres ofensivos, entonces está bien; de lo contrario, debe tener cuidado al manejar los conflictos de nombres.
Stefano Ricciardi
2
¿Qué pasa con los límites de longitud de nombre (y ruta) del sistema de archivos? ¿Qué pasa con los nombres de archivo reservados (PRN CON)? Si necesita almacenar los datos y el nombre original, puede usar 2 archivos con nombres de Guid: guid.txt y guid.dat
Jack
6
Un trazador de líneas, por diversión result = Path.GetInvalidFileNameChars (). Aggregate (result, (current, c) => current.Replace (c, '-'));
Paul Knopf
1
@PaulKnopf, ¿estás seguro de que JetBrain no tiene derechos de autor sobre ese código?)
Marcus
36
Para quitar caracteres no válidos:
staticreadonlychar[] invalidFileNameChars =Path.GetInvalidFileNameChars();// Builds a string out of valid charsvar validFilename =newstring(filename.Where(ch =>!invalidFileNameChars.Contains(ch)).ToArray());
Para reemplazar caracteres no válidos:
staticreadonlychar[] invalidFileNameChars =Path.GetInvalidFileNameChars();// Builds a string out of valid chars and an _ for invalid onesvar validFilename =newstring(filename.Select(ch => invalidFileNameChars.Contains(ch)?'_': ch).ToArray());
Para reemplazar caracteres no válidos (y evitar posibles conflictos de nombres como Hell * vs Hell $):
staticreadonlyIList<char> invalidFileNameChars =Path.GetInvalidFileNameChars();// Builds a string out of valid chars and replaces invalid chars with a unique letter (Moves the Char into the letter range of unicode, starting at "A")var validFilename =newstring(filename.Select(ch => invalidFileNameChars.Contains(ch)?Convert.ToChar(invalidFileNameChars.IndexOf(ch)+65): ch).ToArray());
Esta pregunta se ha hecho muchas veces antes y, como se señaló muchas veces antes, IO.Path.GetInvalidFileNameCharsno es adecuada.
Primero, hay muchos nombres como PRN y CON que están reservados y no permitidos para nombres de archivos. Hay otros nombres que no se permiten solo en la carpeta raíz. Tampoco se permiten los nombres que terminan en un punto.
En segundo lugar, existe una variedad de limitaciones de longitud. Lea la lista completa de NTFS aquí .
En tercer lugar, puede adjuntar a sistemas de archivos que tienen otras limitaciones. Por ejemplo, los nombres de archivo ISO 9660 no pueden comenzar con "-" pero pueden contenerlo.
Cuarto, ¿qué se hace si dos procesos eligen "arbitrariamente" el mismo nombre?
En general, usar nombres generados externamente para nombres de archivos es una mala idea. Sugiero generar sus propios nombres de archivos privados y almacenar internamente nombres legibles por humanos.
Aunque es técnicamente preciso, GetInvalidFileNameChars es bueno para el 80% o más de las situaciones en las que lo usaría, por lo que es una buena respuesta. Creo que su respuesta hubiera sido más apropiada como comentario a la respuesta aceptada.
CubanX
4
Estoy de acuerdo con DourHighArch. Guarde el archivo internamente como una guía, refiérase al "nombre descriptivo" que está almacenado en una base de datos. No permita que los usuarios controlen sus rutas en el sitio web o intentarán robar su web.config. Si incorpora la reescritura de URL para que quede limpio, solo funcionará para URL compatibles que coincidan en la base de datos.
rtpHarry
22
Estoy de acuerdo con Grauenwolf y recomendaría encarecidamente el Path.GetInvalidFileNameChars()
Si quería ser aún más conciso / críptico:Path.GetInvalidFileNameChars().Aggregate(file, (current, c) => current.Replace(c, '-'))
Michael Petito
@ BlueRaja-DannyPflughoeft ¿Porque quieres hacerlo más lento?
Jonathan Allen
@Johnathan Allen, ¿qué te hace pensar que foreach es más rápido que Array.ForEach?
Ryan Buddicom
5
@rbuddicom Array.ForEach toma un delegado, lo que significa que necesita invocar una función que no se puede insertar. Para cadenas cortas, podría terminar gastando más tiempo en la sobrecarga de llamadas a funciones que en la lógica real. .NET Core está buscando formas de "desvirtualizar" las llamadas, reduciendo la sobrecarga.
No estoy seguro de cómo se calcula el resultado de GetInvalidFileNameChars, pero "Get" sugiere que no es trivial, así que guardo los resultados en caché. Además, esto solo atraviesa la cadena de entrada una vez en lugar de varias veces, como las soluciones anteriores que iteran sobre el conjunto de caracteres no válidos, reemplazándolos en la cadena de origen uno a la vez. Además, me gustan las soluciones basadas en dónde, pero prefiero reemplazar los caracteres no válidos en lugar de eliminarlos. Finalmente, mi reemplazo es exactamente un carácter para evitar convertir caracteres en cadenas mientras itero sobre la cadena.
Digo todo eso sin hacer el perfil, este simplemente me "sintió" bien. :)
Si desea eliminar rápidamente todos los caracteres especiales, lo que a veces es más legible por el usuario para los nombres de archivo, esto funciona muy bien:
string myCrazyName ="q`w^e!r@t#y$u%i^o&p*a(s)d_f-g+h=j{k}l|z:x\"c<v>b?n[m]q\\w;e'r,t.y/u";string safeName =Regex.Replace(
myCrazyName,"\W",/*Matches any nonword character. Equivalent to '[^A-Za-z0-9_]'*/"",RegexOptions.IgnoreCase);// safeName == "qwertyuiopasd_fghjklzxcvbnmqwertyu"
en realidad \Wcoincide con más que no alfanuméricos ( [^A-Za-z0-9_]). Todos los caracteres de 'palabra' Unicode (русский 中文 ..., etc.) tampoco serán reemplazados. Pero esto es bueno.
Ismael
El único inconveniente es que esto también se elimina, .por lo que primero debe extraer la extensión y agregarla nuevamente después.
Esto es lo que acabo de agregar a la clase estática StringExtensions de ClipFlair ( http://github.com/Zoomicon/ClipFlair ) (proyecto Utils.Silverlight), según la información recopilada de los enlaces a las preguntas relacionadas con stackoverflow publicadas por Dour High Arch arriba:
publicstaticstringReplaceInvalidFileNameChars(thisstring s,string replacement =""){returnRegex.Replace(s,"["+Regex.Escape(newString(System.IO.Path.GetInvalidPathChars()))+"]",
replacement,//can even use a replacement string of any lengthRegexOptions.IgnoreCase);//not using System.IO.Path.InvalidPathChars (deprecated insecure API)}
privatevoid textBoxFileName_KeyPress(object sender,KeyPressEventArgs e){
e.Handled=CheckFileNameSafeCharacters(e);}/// <summary>/// This is a good function for making sure that a user who is naming a file uses proper characters/// </summary>/// <param name="e"></param>/// <returns></returns>internalstaticboolCheckFileNameSafeCharacters(System.Windows.Forms.KeyPressEventArgs e){if(e.KeyChar.Equals(24)||
e.KeyChar.Equals(3)||
e.KeyChar.Equals(22)||
e.KeyChar.Equals(26)||
e.KeyChar.Equals(25))//Control-X, C, V, Z and Yreturnfalse;if(e.KeyChar.Equals('\b'))//backspacereturnfalse;char[] charArray =Path.GetInvalidFileNameChars();if(charArray.Contains(e.KeyChar))returntrue;//Stop the character from being entered into the control since it is non-numericalelsereturnfalse;}
De mis proyectos anteriores, encontré esta solución, que ha estado funcionando perfectamente durante 2 años. Estoy reemplazando los caracteres ilegales con "!", Y luego verifico si hay dobles !!, use su propio carácter.
publicstringGetSafeFilename(string filename){string res =string.Join("!", filename.Split(Path.GetInvalidFileNameChars()));while(res.IndexOf("!!")>=0)
res = res.Replace("!!","!");return res;}
Muchas respuestas sugieren usar lo Path.GetInvalidFileNameChars()que me parece una mala solución. Te animo a que utilices la lista blanca en lugar de la lista negra porque los piratas informáticos siempre encontrarán una manera de evitarlo.
Aquí hay un ejemplo de código que podría usar:
string whitelist ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";foreach(char c in filename){if(!whitelist.Contains(c)){
filename = filename.Replace(c,'-');}}
Respuestas:
Uf, odio cuando la gente trata de adivinar qué caracteres son válidos. Además de ser completamente no portátil (siempre pensando en Mono), los dos comentarios anteriores perdieron más 25 caracteres no válidos.
fuente
Para quitar caracteres no válidos:
Para reemplazar caracteres no válidos:
Para reemplazar caracteres no válidos (y evitar posibles conflictos de nombres como Hell * vs Hell $):
fuente
Esta pregunta se ha hecho muchas veces antes y, como se señaló muchas veces antes,
IO.Path.GetInvalidFileNameChars
no es adecuada.Primero, hay muchos nombres como PRN y CON que están reservados y no permitidos para nombres de archivos. Hay otros nombres que no se permiten solo en la carpeta raíz. Tampoco se permiten los nombres que terminan en un punto.
En segundo lugar, existe una variedad de limitaciones de longitud. Lea la lista completa de NTFS aquí .
En tercer lugar, puede adjuntar a sistemas de archivos que tienen otras limitaciones. Por ejemplo, los nombres de archivo ISO 9660 no pueden comenzar con "-" pero pueden contenerlo.
Cuarto, ¿qué se hace si dos procesos eligen "arbitrariamente" el mismo nombre?
En general, usar nombres generados externamente para nombres de archivos es una mala idea. Sugiero generar sus propios nombres de archivos privados y almacenar internamente nombres legibles por humanos.
fuente
Estoy de acuerdo con Grauenwolf y recomendaría encarecidamente el
Path.GetInvalidFileNameChars()
Aquí está mi contribución de C #:
PD: esto es más críptico de lo que debería ser, estaba tratando de ser conciso.
fuente
Array.ForEach
foreach
Path.GetInvalidFileNameChars().Aggregate(file, (current, c) => current.Replace(c, '-'))
Esta es mi versión:
No estoy seguro de cómo se calcula el resultado de GetInvalidFileNameChars, pero "Get" sugiere que no es trivial, así que guardo los resultados en caché. Además, esto solo atraviesa la cadena de entrada una vez en lugar de varias veces, como las soluciones anteriores que iteran sobre el conjunto de caracteres no válidos, reemplazándolos en la cadena de origen uno a la vez. Además, me gustan las soluciones basadas en dónde, pero prefiero reemplazar los caracteres no válidos en lugar de eliminarlos. Finalmente, mi reemplazo es exactamente un carácter para evitar convertir caracteres en cadenas mientras itero sobre la cadena.
Digo todo eso sin hacer el perfil, este simplemente me "sintió" bien. :)
fuente
new HashSet<char>(Path.GetInvalidFileNameChars())
para evitar la enumeración O (n): microoptimización.Aquí está la función que estoy usando ahora (gracias jcollum por el ejemplo de C #):
Solo puse esto en una clase de "Ayudantes" por conveniencia.
fuente
Si desea eliminar rápidamente todos los caracteres especiales, lo que a veces es más legible por el usuario para los nombres de archivo, esto funciona muy bien:
fuente
\W
coincide con más que no alfanuméricos ([^A-Za-z0-9_]
). Todos los caracteres de 'palabra' Unicode (русский 中文 ..., etc.) tampoco serán reemplazados. Pero esto es bueno..
por lo que primero debe extraer la extensión y agregarla nuevamente después.fuente
¿Por qué no convertir la cadena a un equivalente Base64 como este?
Si desea volver a convertirlo para poder leerlo:
Usé esto para guardar archivos PNG con un nombre único a partir de una descripción aleatoria.
fuente
Esto es lo que acabo de agregar a la clase estática StringExtensions de ClipFlair ( http://github.com/Zoomicon/ClipFlair ) (proyecto Utils.Silverlight), según la información recopilada de los enlaces a las preguntas relacionadas con stackoverflow publicadas por Dour High Arch arriba:
fuente
fuente
Encuentro que usar esto es rápido y fácil de entender:
Esto funciona porque a
string
esIEnumerable
como unachar
matriz y hay unastring
cadena de constructor que toma unachar
matriz.fuente
De mis proyectos anteriores, encontré esta solución, que ha estado funcionando perfectamente durante 2 años. Estoy reemplazando los caracteres ilegales con "!", Y luego verifico si hay dobles !!, use su propio carácter.
fuente
Muchas respuestas sugieren usar lo
Path.GetInvalidFileNameChars()
que me parece una mala solución. Te animo a que utilices la lista blanca en lugar de la lista negra porque los piratas informáticos siempre encontrarán una manera de evitarlo.Aquí hay un ejemplo de código que podría usar:
fuente