Meu programa pega strings arbitrárias da Internet e as usa para nomes de arquivos. Existe uma maneira simples de remover os caracteres inválidos dessas strings ou preciso escrever uma função personalizada para isso?
Meu programa pega strings arbitrárias da Internet e as usa para nomes de arquivos. Existe uma maneira simples de remover os caracteres inválidos dessas strings ou preciso escrever uma função personalizada para isso?
Respostas:
Ugh, odeio quando as pessoas tentam adivinhar quais caracteres são válidos. Além de serem completamente não portáteis (sempre pensando em Mono), ambos os comentários anteriores perderam mais 25 caracteres invá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 retirar caracteres invá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 substituir 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 substituir caracteres inválidos (e evitar conflito de nome em potencial, como Inferno * vs Inferno $):
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 pergunta já foi feita muitas vezes antes e, como apontado muitas vezes antes, IO.Path.GetInvalidFileNameChars
não é adequada.
Primeiro, existem muitos nomes como PRN e CON que são reservados e não são permitidos para nomes de arquivos. Existem outros nomes não permitidos apenas na pasta raiz. Nomes que terminam em ponto também não são permitidos.
Em segundo lugar, há uma variedade de limitações de comprimento. Leia a lista completa de NTFS aqui .
Terceiro, você pode anexar a sistemas de arquivos que possuem outras limitações. Por exemplo, os nomes de arquivo ISO 9660 não podem começar com "-", mas podem contê-lo.
Quarto, o que você faria se dois processos escolhessem "arbitrariamente" o mesmo nome?
Em geral, usar nomes gerados externamente para nomes de arquivos é uma má ideia. Eu sugiro gerar seus próprios nomes de arquivo privados e armazenar nomes legíveis internamente.
Eu concordo com Grauenwolf e recomendo fortemente o Path.GetInvalidFileNameChars()
Aqui está minha contribuição C #:
string file = @"38?/.\}[+=n a882 a.a*/|n^%$ ad#(-))";
Array.ForEach(Path.GetInvalidFileNameChars(),
c => file = file.Replace(c.ToString(), String.Empty));
ps - isso é mais enigmático do que deveria ser - eu estava tentando ser conciso.
Array.ForEach
vez de apenas foreach
aqui
Path.GetInvalidFileNameChars().Aggregate(file, (current, c) => current.Replace(c, '-'))
Aqui está minha versão:
static string GetSafeFileName(string name, char replace = '_') {
char[] invalids = Path.GetInvalidFileNameChars();
return new string(name.Select(c => invalids.Contains(c) ? replace : c).ToArray());
}
Não tenho certeza de como o resultado de GetInvalidFileNameChars é calculado, mas o "Get" sugere que não é trivial, por isso coloco os resultados em cache. Além disso, isso só percorre a string de entrada uma vez em vez de várias vezes, como as soluções acima que iteram sobre o conjunto de caracteres inválidos, substituindo-os na string de origem, um de cada vez. Além disso, gosto das soluções baseadas em Where, mas prefiro substituir caracteres inválidos em vez de removê-los. Por fim, minha substituição é exatamente um caractere para evitar a conversão de caracteres em strings à medida que faço a iteração na string.
Eu digo tudo isso sem fazer o perfil - este apenas "pareceu" bom para mim. :)
new HashSet<char>(Path.GetInvalidFileNameChars())
para evitar a enumeração O (n) - micro-otimização.
Esta é a função que estou usando agora (obrigado jcollum pelo exemplo C #):
public static string MakeSafeFilename(string filename, char replaceChar)
{
foreach (char c in System.IO.Path.GetInvalidFileNameChars())
{
filename = filename.Replace(c, replaceChar);
}
return filename;
}
Acabei de colocar isso em uma classe de "Ajudantes" por conveniência.
Se você quiser remover rapidamente todos os caracteres especiais, o que às vezes é mais legível pelo usuário para nomes de arquivo, isso funciona bem:
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
corresponde a mais do que não-alfa-numéricos ( [^A-Za-z0-9_]
). Todos os caracteres de 'palavra' Unicode (русский 中文 ..., etc.) também não serão substituídos. Mas isso é uma coisa boa.
.
então você precisa extrair a extensão primeiro e adicioná-la novamente depois.
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 que não converter a string em um equivalente em Base64 como este:
string UnsafeFileName = "salmnas dlajhdla kjha;dmas'lkasn";
string SafeFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(UnsafeFileName));
Se você quiser convertê-lo de volta para que possa lê-lo:
UnsafeFileName = Encoding.UTF8.GetString(Convert.FromBase64String(SafeFileName));
Usei isso para salvar arquivos PNG com um nome exclusivo de uma descrição aleatória.
Aqui está o que eu adicionei para (de ClipFlair http://github.com/Zoomicon/ClipFlair ) StringExtensions classe estática (projeto Utils.Silverlight), com base em informações recolhidas a partir dos links para outras questões relacionadas stackoverflow postados por Dour High Arch acima:
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;
}
Acho que usar isso é rápido e 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
Isso funciona porque a string
é IEnumerable
uma char
matriz e há uma string
string de construtor que recebe uma char
matriz.
Em meus projetos mais antigos, encontrei essa solução, que está funcionando perfeitamente há 2 anos. Estou substituindo chars ilegais por "!", E então checo por !! 's, use seu próprio char.
public string GetSafeFilename(string filename)
{
string res = string.Join("!", filename.Split(Path.GetInvalidFileNameChars()));
while (res.IndexOf("!!") >= 0)
res = res.Replace("!!", "!");
return res;
}
Muitas respostas sugerem o uso, o Path.GetInvalidFileNameChars()
que parece uma solução ruim para mim. Eu encorajo você a usar a lista de permissões em vez de lista negra porque os hackers sempre encontrarão uma maneira de contornar isso.
Aqui está um exemplo de código que você pode usar:
string whitelist = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
foreach (char c in filename)
{
if (!whitelist.Contains(c))
{
filename = filename.Replace(c, '-');
}
}