Existe uma maneira de tornar o caminho do arquivo de strings seguro em c #?
92
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?
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 filenameDim filename AsString="salmnas dlajhdla kjha;dmas'lkasn"ForEach c In IO.Path.GetInvalidFileNameChars
filename = filename.Replace(c,"")Next'See also IO.Path.GetInvalidPathChars
A versão C #: foreach (var c in Path.GetInvalidFileNameChars ()) {fileName = fileName.Replace (c, '-'); }
jcollum
8
Como esta solução lidaria com conflitos de nomes? Parece que mais de uma string pode corresponder a um único nome de arquivo ("Inferno?" E "Inferno *", por exemplo). Se você está ok para remover apenas caracteres ofensivos, tudo bem; caso contrário, você precisa ter cuidado ao lidar com conflitos de nomes.
Stefano Ricciardi
2
e quanto aos limites de tamanho do nome (e caminho) do sistema de arquivos? e os nomes de arquivo reservados (PRN CON)? Se precisar armazenar os dados e o nome original, você pode usar 2 arquivos com nomes de Guid: guid.txt e guid.dat
Jack
6
Uma linha, para diversão resultado = Path.GetInvalidFileNameChars (). Agregar (resultado, (corrente, c) => corrente.Replace (c, '-'));
Paul Knopf
1
@PaulKnopf, você tem certeza de que o JetBrain não possui direitos autorais sobre esse código;)
Marcus
36
Para retirar caracteres inválidos:
staticreadonlychar[] invalidFileNameChars =Path.GetInvalidFileNameChars();// Builds a string out of valid charsvar validFilename =newstring(filename.Where(ch =>!invalidFileNameChars.Contains(ch)).ToArray());
Para substituir caracteres inválidos:
staticreadonlychar[] invalidFileNameChars =Path.GetInvalidFileNameChars();// Builds a string out of valid chars and an _ for invalid onesvar validFilename =newstring(filename.Select(ch => invalidFileNameChars.Contains(ch)?'_': ch).ToArray());
Para substituir caracteres inválidos (e evitar conflito de nome em potencial, como Inferno * vs Inferno $):
staticreadonlyIList<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 =newstring(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.GetInvalidFileNameCharsnã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.
Embora você seja tecnicamente preciso, GetInvalidFileNameChars é bom para 80% + das situações em que você o usaria, portanto, é uma boa resposta. Sua resposta teria sido mais apropriada como um comentário à resposta aceita, eu acho.
CubanX
4
Eu concordo com DourHighArch. Salve o arquivo internamente como um guia, comparando-o com o "nome amigável" que está armazenado em um banco de dados. Não deixe que os usuários controlem seus caminhos no site ou eles tentarão roubar seu web.config. Se você incorporar a reescrita de url para torná-la limpa, ela só funcionará para urls amigáveis correspondentes no banco de dados.
rtpHarry
22
Eu concordo com Grauenwolf e recomendo fortemente o Path.GetInvalidFileNameChars()
Por que no mundo você usaria em Array.ForEachvez de apenas foreachaqui
BlueRaja - Danny Pflughoeft
9
Se você quiser ser ainda mais conciso / enigmático:Path.GetInvalidFileNameChars().Aggregate(file, (current, c) => current.Replace(c, '-'))
Michael Petito
@ BlueRaja-DannyPflughoeft Porque você quer torná-lo mais lento?
Jonathan Allen
@Johnathan Allen, o que o faz pensar que foreach é mais rápido do que Array.ForEach?
Ryan Buddicom
5
@rbuddicom Array.ForEach recebe um delegado, o que significa que ele precisa invocar uma função que não pode ser embutida. Para strings curtas, você pode acabar gastando mais tempo na sobrecarga da chamada de função do que na lógica real. O .NET Core está procurando maneiras de "desvirtualizar" as chamadas, reduzindo a sobrecarga.
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. :)
na verdade, \Wcorresponde 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.
Ishmael
A única desvantagem é que isso também remove, .então você precisa extrair a extensão primeiro e adicioná-la novamente depois.
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:
publicstaticstringReplaceInvalidFileNameChars(thisstring s,string replacement =""){returnRegex.Replace(s,"["+Regex.Escape(newString(System.IO.Path.GetInvalidPathChars()))+"]",
replacement,//can even use a replacement string of any lengthRegexOptions.IgnoreCase);//not using System.IO.Path.InvalidPathChars (deprecated insecure API)}
privatevoid 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>internalstaticboolCheckFileNameSafeCharacters(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 Yreturnfalse;if(e.KeyChar.Equals('\b'))//backspacereturnfalse;char[] charArray =Path.GetInvalidFileNameChars();if(charArray.Contains(e.KeyChar))returntrue;//Stop the character from being entered into the control since it is non-numericalelsereturnfalse;}
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.
publicstringGetSafeFilename(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,'-');}}
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.
fonte
Para retirar caracteres inválidos:
Para substituir caracteres inválidos:
Para substituir caracteres inválidos (e evitar conflito de nome em potencial, como Inferno * vs Inferno $):
fonte
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.
fonte
Eu concordo com Grauenwolf e recomendo fortemente o
Path.GetInvalidFileNameChars()
Aqui está minha contribuição C #:
ps - isso é mais enigmático do que deveria ser - eu estava tentando ser conciso.
fonte
Array.ForEach
vez de apenasforeach
aquiPath.GetInvalidFileNameChars().Aggregate(file, (current, c) => current.Replace(c, '-'))
Aqui está minha versão:
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. :)
fonte
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 #):
Acabei de colocar isso em uma classe de "Ajudantes" por conveniência.
fonte
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:
fonte
\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.fonte
Por que não converter a string em um equivalente em Base64 como este:
Se você quiser convertê-lo de volta para que possa lê-lo:
Usei isso para salvar arquivos PNG com um nome exclusivo de uma descrição aleatória.
fonte
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:
fonte
fonte
Acho que usar isso é rápido e fácil de entender:
Isso funciona porque a
string
éIEnumerable
umachar
matriz e há umastring
string de construtor que recebe umachar
matriz.fonte
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.
fonte
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:
fonte