trenzado - ¿Hay alguna manera de hacer que las cadenas de ruta de archivos sean seguras en c#?
como hacer collares faciles (12)
¿Por qué no convertir la cadena a un equivalente de Base64 como este?
string UnsafeFileName = "salmnas dlajhdla kjha;dmas''lkasn";
string SafeFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(UnsafeFileName));
Si quieres convertirlo de nuevo para que lo puedas leer:
UnsafeFileName = Encoding.UTF8.GetString(Convert.FromBase64String(SafeFileName));
Lo usé para guardar archivos PNG con un nombre único de una descripción aleatoria.
Mi programa tomará cadenas arbitrarias de internet y las usará para nombres de archivos. ¿Hay alguna manera simple de eliminar los caracteres incorrectos de estas cadenas o debo escribir una función personalizada para esto?
Aquí está la función que estoy usando ahora (gracias jcollum por el ejemplo C #):
public static string MakeSafeFilename(string filename, char replaceChar)
{
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
filename = filename.Replace(c, replaceChar);
}
return filename;
}
Acabo de poner esto en una clase de "Ayudantes" para mayor comodidad.
Aquí está mi versión:
static string GetSafeFileName(string name, char replace = ''_'') {
char[] invalids = Path.GetInvalidFileNameChars();
return new string(name.Select(c => invalids.Contains(c) ? replace : c).ToArray());
}
No estoy seguro de cómo se calcula el resultado de GetInvalidFileNameChars, pero el "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 inválidos, reemplazándolos en la cadena de origen de a uno por vez. Además, me gustan las soluciones basadas en Where, pero prefiero reemplazar los caracteres inválidos en lugar de eliminarlos. Finalmente, mi reemplazo es exactamente un carácter para evitar la conversión de caracteres a cadenas a medida que repito sobre la cadena.
Digo todo eso sin hacer el perfil, este simplemente me "hizo sentir bien". :)
Esta pregunta se ha hecho many times before y, como se ha señalado muchas veces antes, IO.Path.GetInvalidFileNameChars
no es adecuado.
En primer lugar, hay muchos nombres como PRN y CON que están reservados y no están permitidos para los nombres de archivo. Hay otros nombres no permitidos solo en la carpeta raíz. Los nombres que terminan en un período tampoco están permitidos.
En segundo lugar, hay una variedad de limitaciones de longitud. Lea la lista completa de NTFS here .
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.
En cuarto lugar, ¿qué haces si dos procesos "arbitrariamente" eligen el mismo nombre?
En general, el uso de nombres generados externamente para nombres de archivos es una mala idea. Sugiero generar sus propios nombres de archivos privados y almacenar nombres legibles por humanos internamente.
Esto es lo que acabo de agregar a la clase estática de ClipFlair ( http://clipflair.codeplex.com ) StringExtensions (proyecto UtilsSilverlight), en base a la información recopilada de los enlaces a las preguntas relacionadas con el publicadas por Dour High Arch arriba:
public static string ReplaceInvalidFileNameChars(this string s, string replacement = "")
{
return Regex.Replace(s,
"[" + Regex.Escape(new String(System.IO.Path.GetInvalidPathChars())) + "]",
replacement, //can even use a replacement string of any length
RegexOptions.IgnoreCase);
//not using System.IO.Path.InvalidPathChars (deprecated insecure API)
}
Estoy de acuerdo con Grauenwolf y recomendaría mucho Path.GetInvalidFileNameChars()
Aquí está mi contribución de C #:
string file = @"38?/./}[+=n a882 a.a*/|n^%$ ad#(-))";
Array.ForEach(Path.GetInvalidFileNameChars(),
c => file = file.Replace(c.ToString(), String.Empty));
ps - esto es más críptico de lo que debería ser - estaba tratando de ser conciso.
Me parece que usar esto es rápido y fácil de entender:
<Extension()>
Public Function MakeSafeFileName(FileName As String) As String
Return FileName.Where(Function(x) Not IO.Path.GetInvalidFileNameChars.Contains(x)).ToArray
End Function
Esto funciona porque una string
es IEnumerable
como una matriz char
y hay una string
constructor de cadena que toma una matriz char
.
Para quitar los caracteres no válidos:
static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
// Builds a string out of valid chars
var validFilename = new string(filename.Where(ch => !invalidFileNameChars.Contains(ch)).ToArray());
Para reemplazar caracteres inválidos:
static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
// Builds a string out of valid chars and an _ for invalid ones
var validFilename = new string(filename.Select(ch => invalidFileNameChars.Contains(ch) ? ''_'' : ch).ToArray());
Para reemplazar caracteres no válidos (y evitar posibles conflictos de nombres como Hell * vs Hell $):
static readonly IList<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 = new string(filename.Select(ch => invalidFileNameChars.Contains(ch) ? Convert.ToChar(invalidFileNameChars.IndexOf(ch) + 65) : ch).ToArray());
Si desea eliminar rápidamente todos los caracteres especiales que a veces son más legibles por el usuario para los nombres de los archivos, 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"
Uf, lo odio cuando las personas intentan 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 de 25 caracteres no válidos.
''Clean just a filename
Dim filename As String = "salmnas dlajhdla kjha;dmas''lkasn"
For Each c In IO.Path.GetInvalidFileNameChars
filename = filename.Replace(c, "")
Next
''See also IO.Path.GetInvalidPathChars
private void 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>
internal static bool CheckFileNameSafeCharacters(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 Y
return false;
if (e.KeyChar.Equals(''/b''))//backspace
return false;
char[] charArray = Path.GetInvalidFileNameChars();
if (charArray.Contains(e.KeyChar))
return true;//Stop the character from being entered into the control since it is non-numerical
else
return false;
}
static class Utils
{
public static string MakeFileSystemSafe(this string s)
{
return new string(s.Where(IsFileSystemSafe).ToArray());
}
public static bool IsFileSystemSafe(char c)
{
return !Path.GetInvalidFileNameChars().Contains(c);
}
}