Pergunta

Meu programa levará cordas arbitrárias da internet e usá-los para nomes de arquivo. Existe uma maneira simples de remover os personagens maus destas cordas ou que eu preciso para escrever uma função personalizada para isso?

Foi útil?

Solução

Ugh, eu odeio quando as pessoas tentam adivinhar quais caracteres são válidos. Além de ser completamente não-portátil (sempre pensando em Mono), ambos dos comentários anteriores perdeu mais caracteres inválidos 25.

'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

Outras dicas

Esta pergunta foi feita muitos vezes antes e, como apontado muitas vezes antes, IO.Path.GetInvalidFileNameChars não é adequada.

Em primeiro lugar, há muitos nomes como PRN e CON que são reservados e não permitidos para nomes de arquivos. Há outros nomes não permitidos apenas na pasta raiz. Nomes que terminam em um período também não são permitidos.

Em segundo lugar, há uma variedade de limitações de comprimento. Leia a lista completa para NTFS aqui .

Em terceiro lugar, você pode anexar a sistemas de arquivos que têm outras limitações. Por exemplo, ISO 9660 nomes de arquivos não pode começar com "-"., Mas pode contê-lo

Em quarto lugar, o que você faria se dois processos "arbitrariamente" escolher o mesmo nome?

Em geral, o uso de nomes externamente gerados para nomes de arquivos é uma idéia ruim. Sugiro gerando seus próprios nomes de arquivo privado e armazenar nomes legíveis internamente.

Para remover 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 potenciais conflitos nome como o 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());

Eu concordo com Grauenwolf e recomendo a Path.GetInvalidFileNameChars()

Aqui está o meu C # contribuição:

string file = @"38?/.\}[+=n a882 a.a*/|n^%$ ad#(-))";
Array.ForEach(Path.GetInvalidFileNameChars(), 
      c => file = file.Replace(c.ToString(), String.Empty));

P.S. - este é mais crítico do que deveria ser - Eu estava tentando ser conciso

.

Aqui está a 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());
}

Eu não estou certo de como o resultado de GetInvalidFileNameChars é calculada, mas o "Get" sugere que é não-trivial, assim que eu armazenar em cache os resultados. Além disso, isso só atravessa a cadeia de entrada uma vez, em vez de várias vezes, como as soluções acima que iterar sobre o conjunto de caracteres inválidos, substituindo-os na única cadeia de origem de cada vez. Além disso, eu gosto das soluções baseadas em Onde, mas eu prefiro para substituir caracteres inválidos em vez de removê-los. Finalmente, minha substituição é exatamente um personagem para evitar a conversão de caracteres para cadeias como eu iterar sobre a corda.

Eu digo tudo o que w / o a fazer o profiling - este apenas "sentida" bom para mim. :)

Aqui está a função que estou usando agora (graças jcollum para o 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 "ajudantes" por conveniência.

Se você quer retirar rapidamente todos os caracteres especiais que às vezes é mais legível do usuário para nomes de arquivos isso funciona muito 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"
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);
    }
}

Aqui está o que eu apenas adicionado ao do ClipFlair ( http://github.com/Zoomicon/ClipFlair ) StringExtensions classe estática (projeto Utils.Silverlight), com base em informações recolhidas a partir dos links para perguntas stackoverflow relacionados 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)
}

Por que não converter a string para um equivalente Base64 como esta:

string UnsafeFileName = "salmnas dlajhdla kjha;dmas'lkasn";
string SafeFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(UnsafeFileName));

Se você quiser convertê-lo novamente para que você possa lê-lo:

UnsafeFileName = Encoding.UTF8.GetString(Convert.FromBase64String(SafeFileName));

Eu usei isso para salvar arquivos PNG com um nome exclusivo de uma descrição aleatória.

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 usando isso para ser 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 como uma matriz char e há uma seqüência de construtor string que leva uma matriz char.

Muitos anwer sugerir a utilização Path.GetInvalidFileNameChars() que parece ser uma solução ruim para mim. Encorajo-vos a usar whitelisting em vez de lista negra porque os hackers sempre vai encontrar uma maneira, eventualmente, para ignorá-lo.

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, '-');
        }
    }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top