Il mio programma prenderà stringhe arbitrarie da Internet e le utilizzerà per i nomi dei file. C'è un modo semplice per rimuovere i caratteri cattivi da queste stringhe o devo scrivere una funzione personalizzata per questo?
Il mio programma prenderà stringhe arbitrarie da Internet e le utilizzerà per i nomi dei file. C'è un modo semplice per rimuovere i caratteri cattivi da queste stringhe o devo scrivere una funzione personalizzata per questo?
Risposte:
Uffa, odio quando le persone cercano di indovinare quali caratteri sono validi. Oltre ad essere completamente non portabili (pensando sempre a Mono), entrambi i commenti precedenti mancavano di più di 25 caratteri non validi.
'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
Per rimuovere i caratteri non validi:
static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
// Builds a string out of valid chars
var validFilename = new string(filename.Where(ch => !invalidFileNameChars.Contains(ch)).ToArray());
Per sostituire i caratteri non validi:
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());
Per sostituire i caratteri non validi (ed evitare potenziali conflitti di nome come 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());
Questa domanda è stata posta molte volte in passato e, come sottolineato molte volte, IO.Path.GetInvalidFileNameChars
non è adeguata.
Innanzitutto, ci sono molti nomi come PRN e CON che sono riservati e non consentiti per i nomi di file. Ci sono altri nomi non consentiti solo nella cartella principale. Anche i nomi che terminano con un punto non sono consentiti.
In secondo luogo, ci sono una serie di limitazioni di lunghezza. Leggi l'elenco completo per NTFS qui .
Terzo, puoi collegarti a filesystem che hanno altre limitazioni. Ad esempio, i nomi di file ISO 9660 non possono iniziare con "-" ma possono contenerlo.
Quarto, cosa fare se due processi scelgono "arbitrariamente" lo stesso nome?
In generale, l'utilizzo di nomi generati esternamente per i nomi di file è una cattiva idea. Suggerisco di generare i propri nomi di file privati e di memorizzare internamente i nomi leggibili dall'uomo.
Sono d'accordo con Grauenwolf e consiglio vivamente il Path.GetInvalidFileNameChars()
Ecco il mio contributo in C #:
string file = @"38?/.\}[+=n a882 a.a*/|n^%$ ad#(-))";
Array.ForEach(Path.GetInvalidFileNameChars(),
c => file = file.Replace(c.ToString(), String.Empty));
ps - questo è più criptico di quanto dovrebbe essere - stavo cercando di essere conciso.
Array.ForEach
foreach
Path.GetInvalidFileNameChars().Aggregate(file, (current, c) => current.Replace(c, '-'))
Ecco la mia versione:
static string GetSafeFileName(string name, char replace = '_') {
char[] invalids = Path.GetInvalidFileNameChars();
return new string(name.Select(c => invalids.Contains(c) ? replace : c).ToArray());
}
Non sono sicuro di come viene calcolato il risultato di GetInvalidFileNameChars, ma "Get" suggerisce che non è banale, quindi memorizzo i risultati nella cache. Inoltre, questo attraversa la stringa di input solo una volta anziché più volte, come le soluzioni precedenti che ripetono l'insieme di caratteri non validi, sostituendoli nella stringa di origine uno alla volta. Inoltre, mi piacciono le soluzioni basate su dove, ma preferisco sostituire i caratteri non validi invece di rimuoverli. Infine, la mia sostituzione è esattamente un carattere per evitare di convertire i caratteri in stringhe mentre iterare sulla stringa.
Dico tutto ciò senza fare il profilo - questo mi "è sembrato" carino. :)
new HashSet<char>(Path.GetInvalidFileNameChars())
per evitare l'enumerazione O (n) - microottimizzazione.
Ecco la funzione che sto usando ora (grazie jcollum per l'esempio C #):
public static string MakeSafeFilename(string filename, char replaceChar)
{
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
filename = filename.Replace(c, replaceChar);
}
return filename;
}
L'ho inserito in una classe "Aiutanti" per comodità.
Se vuoi rimuovere rapidamente tutti i caratteri speciali che a volte sono più leggibili dall'utente per i nomi dei file, questo funziona bene:
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
corrisponde a più di non alfa-numerici ( [^A-Za-z0-9_]
). Neanche tutti i caratteri "parola" Unicode (русский 中文 ..., ecc.) Verranno sostituiti. Ma questa è una buona cosa.
.
quindi devi prima estrarre l'estensione e aggiungerla di nuovo dopo.
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);
}
}
Perché non convertire la stringa in un equivalente Base64 come questo:
string UnsafeFileName = "salmnas dlajhdla kjha;dmas'lkasn";
string SafeFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(UnsafeFileName));
Se vuoi riconvertirlo in modo da poterlo leggere:
UnsafeFileName = Encoding.UTF8.GetString(Convert.FromBase64String(SafeFileName));
L'ho usato per salvare file PNG con un nome univoco da una descrizione casuale.
Ecco cosa ho appena aggiunto alla classe statica StringExtensions di ClipFlair ( http://github.com/Zoomicon/ClipFlair ) (progetto Utils.Silverlight), in base alle informazioni raccolte dai collegamenti alle domande di stackoverflow correlate pubblicate da Dour High Arch sopra:
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;
}
Trovo che l'utilizzo di questo sia veloce e facile da capire:
<Extension()>
Public Function MakeSafeFileName(FileName As String) As String
Return FileName.Where(Function(x) Not IO.Path.GetInvalidFileNameChars.Contains(x)).ToArray
End Function
Questo funziona perché a string
è IEnumerable
come un char
array e c'è una string
stringa di costruzione che accetta un char
array.
Dai miei vecchi progetti, ho trovato questa soluzione, che funziona perfettamente da oltre 2 anni. Sostituisco i caratteri illegali con "!", Quindi controllo i doppi !!, usa il tuo carattere.
public string GetSafeFilename(string filename)
{
string res = string.Join("!", filename.Split(Path.GetInvalidFileNameChars()));
while (res.IndexOf("!!") >= 0)
res = res.Replace("!!", "!");
return res;
}
Molte risposte suggeriscono di utilizzare il Path.GetInvalidFileNameChars()
che mi sembra una cattiva soluzione. Ti incoraggio a utilizzare la whitelist anziché la blacklist perché gli hacker troveranno sempre un modo per aggirarla.
Ecco un esempio di codice che potresti usare:
string whitelist = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
foreach (char c in filename)
{
if (!whitelist.Contains(c))
{
filename = filename.Replace(c, '-');
}
}