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?
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?
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.
'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
Para quitar 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 no vá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());
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.
Estoy de acuerdo con Grauenwolf y recomendaría encarecidamente el 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));
PD: esto es más críptico de lo que debería ser, estaba tratando de ser conciso.
Array.ForEach
foreach
Path.GetInvalidFileNameChars().Aggregate(file, (current, c) => current.Replace(c, '-'))
Esta es 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 "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. :)
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 #):
public static string MakeSafeFilename(string filename, char replaceChar)
{
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
filename = filename.Replace(c, replaceChar);
}
return filename;
}
Solo puse esto en una clase de "Ayudantes" por conveniencia.
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"
\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.
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);
}
}
¿Por qué no convertir la cadena a un equivalente Base64 como este?
string UnsafeFileName = "salmnas dlajhdla kjha;dmas'lkasn";
string SafeFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(UnsafeFileName));
Si desea volver a convertirlo para poder leerlo:
UnsafeFileName = Encoding.UTF8.GetString(Convert.FromBase64String(SafeFileName));
Usé esto para guardar archivos PNG con un nombre único a partir de una descripción aleatoria.
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:
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)
}
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;
}
Encuentro 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 a string
es IEnumerable
como una char
matriz y hay una string
cadena de constructor que toma una char
matriz.
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.
public string GetSafeFilename(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, '-');
}
}