Domanda

Il mio programma prenderà stringhe arbitrarie da Internet e le userà per i nomi dei file. Esiste un modo semplice per rimuovere i caratteri errati da queste stringhe o devo scrivere una funzione personalizzata per questo?

È stato utile?

Soluzione

Ugh, lo odio quando le persone cercano di indovinare quali personaggi sono validi. Oltre ad essere completamente non portatile (pensando sempre a Mono), entrambi i commenti precedenti hanno perso 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

Altri suggerimenti

Questa domanda è stata posta molti volte prima e, come sottolineato molte volte prima, IO.Path.GetInvalidFileNameChars non è adeguata.

Innanzitutto, ci sono molti nomi come PRN e CON che sono riservati e non consentiti per i nomi dei file. Esistono altri nomi non consentiti solo nella cartella principale. Anche i nomi che terminano in un punto non sono consentiti.

In secondo luogo, ci sono una varietà di limiti di lunghezza. Leggi l'elenco completo di NTFS qui .

Terzo, puoi collegarti a filesystem che hanno altre limitazioni. Ad esempio, i nomi dei file ISO 9660 non possono iniziare con " - " ma può contenerlo.

In quarto luogo, cosa fai se due processi "arbitrariamente" scegli lo stesso nome?

In generale, usare nomi generati esternamente per i nomi di file è una cattiva idea. Suggerisco di generare i propri nomi di file privati ??e di memorizzare internamente nomi leggibili dall'uomo.

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 nomi 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());

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));

P.S. - questo è più criptico di quanto dovrebbe essere - Stavo cercando di essere conciso.

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 venga calcolato il risultato di GetInvalidFileNameChars, ma il " Ottieni " suggerisce che non è banale, quindi memorizzo nella cache i risultati. Inoltre, ciò attraversa la stringa di input solo una volta anziché più volte, come le soluzioni sopra che ripetono l'insieme di caratteri non validi, sostituendoli uno alla volta nella stringa di origine. Inoltre, mi piacciono le soluzioni basate su Where, 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 eseguo l'iterazione sulla stringa.

Dico tutto ciò senza fare la profilazione - questo solo "sentito". Gentile con me. :)

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 appena inserito in un " Helpers " classe per comodità.

Se vuoi rimuovere rapidamente tutti i caratteri speciali che a volte sono più leggibili dall'utente per i nomi dei file, 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"
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);
    }
}

Ecco cosa ho appena aggiunto a ClipFlair ( http://github.com/Zoomicon/ClipFlair ) Classe statica StringExtensions (progetto Utils.Silverlight), basata sulle informazioni raccolte dai collegamenti alle domande stackover 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)
}

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 i file PNG con un nome univoco da una descrizione casuale.

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 usare 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

Funziona perché una stringa è IEnumerable come un array char e c'è una stringa di costruzione string che accetta un array char .

Molti anwer suggeriscono di usare 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 aggirarlo.

Ecco un esempio di codice che potresti usare:

    string whitelist = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
    foreach (char c in filename)
    {
        if (!whitelist.Contains(c))
        {
            filename = filename.Replace(c, '-');
        }
    }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top