Mon programme prendra des chaînes arbitraires d'Internet et les utilisera pour les noms de fichiers. Existe-t-il un moyen simple de supprimer les mauvais caractères de ces chaînes ou dois-je écrire une fonction personnalisée pour cela?
Mon programme prendra des chaînes arbitraires d'Internet et les utilisera pour les noms de fichiers. Existe-t-il un moyen simple de supprimer les mauvais caractères de ces chaînes ou dois-je écrire une fonction personnalisée pour cela?
Réponses:
Ugh, je déteste quand les gens essaient de deviner quels caractères sont valides. En plus d'être complètement non portables (en pensant toujours à Mono), les deux commentaires précédents ont manqué plus de 25 caractères invalides.
'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
Pour supprimer les caractères non valides:
static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
// Builds a string out of valid chars
var validFilename = new string(filename.Where(ch => !invalidFileNameChars.Contains(ch)).ToArray());
Pour remplacer des caractères non valides:
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());
Pour remplacer les caractères invalides (et éviter les conflits de noms potentiels comme 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());
Cette question a été posée plusieurs fois auparavant et, comme cela a déjà été souligné à maintes reprises,IO.Path.GetInvalidFileNameChars
ne suffit pas.
Premièrement, il existe de nombreux noms comme PRN et CON qui sont réservés et non autorisés pour les noms de fichiers. Il existe d'autres noms non autorisés uniquement dans le dossier racine. Les noms qui se terminent par un point ne sont pas non plus autorisés.
Deuxièmement, il existe diverses limitations de longueur. Lisez la liste complète pour NTFS ici .
Troisièmement, vous pouvez vous attacher à des systèmes de fichiers qui ont d'autres limitations. Par exemple, les noms de fichiers ISO 9660 ne peuvent pas commencer par "-" mais peuvent le contenir.
Quatrièmement, que faites-vous si deux processus choisissent «arbitrairement» le même nom?
En général, l'utilisation de noms générés en externe pour les noms de fichiers est une mauvaise idée. Je suggère de générer vos propres noms de fichiers privés et de stocker des noms lisibles par l'homme en interne.
Je suis d'accord avec Grauenwolf et je recommande vivement le Path.GetInvalidFileNameChars()
Voici ma contribution C #:
string file = @"38?/.\}[+=n a882 a.a*/|n^%$ ad#(-))";
Array.ForEach(Path.GetInvalidFileNameChars(),
c => file = file.Replace(c.ToString(), String.Empty));
ps - c'est plus cryptique qu'il ne devrait l'être - j'essayais d'être concis.
Array.ForEach
foreach
Path.GetInvalidFileNameChars().Aggregate(file, (current, c) => current.Replace(c, '-'))
Voici ma version:
static string GetSafeFileName(string name, char replace = '_') {
char[] invalids = Path.GetInvalidFileNameChars();
return new string(name.Select(c => invalids.Contains(c) ? replace : c).ToArray());
}
Je ne sais pas comment le résultat de GetInvalidFileNameChars est calculé, mais le "Get" suggère qu'il n'est pas trivial, donc je cache les résultats. En outre, cela ne parcourt la chaîne d'entrée qu'une seule fois au lieu de plusieurs fois, comme les solutions ci-dessus qui itèrent sur l'ensemble des caractères non valides, en les remplaçant dans la chaîne source un par un. Aussi, j'aime les solutions basées sur Where, mais je préfère remplacer les caractères invalides au lieu de les supprimer. Enfin, mon remplacement est exactement un caractère pour éviter de convertir des caractères en chaînes lorsque j'itère sur la chaîne.
Je dis tout cela sans faire le profilage - celui-ci "me sentait" juste. :)
new HashSet<char>(Path.GetInvalidFileNameChars())
éviter l'énumération O (n) - micro-optimisation.
Voici la fonction que j'utilise maintenant (merci jcollum pour l'exemple C #):
public static string MakeSafeFilename(string filename, char replaceChar)
{
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
filename = filename.Replace(c, replaceChar);
}
return filename;
}
Je viens de mettre cela dans une classe "Helpers" pour plus de commodité.
Si vous souhaitez supprimer rapidement tous les caractères spéciaux, ce qui est parfois plus lisible par l'utilisateur pour les noms de fichiers, cela fonctionne 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
correspond en fait à plus que les non-alphanumériques ( [^A-Za-z0-9_]
). Tous les caractères Unicode 'mot' (русский 中文 ..., etc.) ne seront pas non plus remplacés. Mais c'est une bonne chose.
.
, vous devez d'abord extraire l'extension, puis l'ajouter à nouveau aprè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);
}
}
Pourquoi ne pas convertir la chaîne en un équivalent Base64 comme ceci:
string UnsafeFileName = "salmnas dlajhdla kjha;dmas'lkasn";
string SafeFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(UnsafeFileName));
Si vous souhaitez le reconvertir pour pouvoir le lire:
UnsafeFileName = Encoding.UTF8.GetString(Convert.FromBase64String(SafeFileName));
Je l'ai utilisé pour enregistrer des fichiers PNG avec un nom unique à partir d'une description aléatoire.
Voici ce que je viens d'ajouter à la classe statique StringExtensions de ClipFlair ( http://github.com/Zoomicon/ClipFlair ) (projet Utils.Silverlight), sur la base des informations recueillies à partir des liens vers les questions liées au stackoverflow publiées par Dour High Arch ci-dessus:
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;
}
Je trouve que l'utilisation de ceci est rapide et facile à comprendre:
<Extension()>
Public Function MakeSafeFileName(FileName As String) As String
Return FileName.Where(Function(x) Not IO.Path.GetInvalidFileNameChars.Contains(x)).ToArray
End Function
Cela fonctionne car a string
est en IEnumerable
tant que char
tableau et qu'il existe une string
chaîne de constructeur qui prend un char
tableau.
De mes projets plus anciens, j'ai trouvé cette solution qui fonctionne parfaitement depuis 2 ans. Je remplace les caractères illégaux par "!", Puis vérifie les doubles !!, utilise votre propre caractère.
public string GetSafeFilename(string filename)
{
string res = string.Join("!", filename.Split(Path.GetInvalidFileNameChars()));
while (res.IndexOf("!!") >= 0)
res = res.Replace("!!", "!");
return res;
}
Beaucoup de réponses suggèrent d'utiliser Path.GetInvalidFileNameChars()
ce qui me semble être une mauvaise solution. Je vous encourage à utiliser la liste blanche au lieu de la liste noire, car les pirates trouveront toujours un moyen de la contourner.
Voici un exemple de code que vous pourriez utiliser:
string whitelist = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
foreach (char c in filename)
{
if (!whitelist.Contains(c))
{
filename = filename.Replace(c, '-');
}
}