C # 'da dizeleri dosya yolunu güvenli hale getirmenin bir yolu var mı?


Yanıtlar:


172

Ugh, insanların hangi karakterlerin geçerli olduğunu tahmin etmesinden nefret ediyorum. Tamamen taşınabilir olmamasının yanı sıra (her zaman Mono'yu düşünür), önceki yorumların her ikisi de 25'ten fazla geçersiz karakteri kaçırdı.

'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

84
C # sürümü: foreach (Path.GetInvalidFileNameChars () 'da var c) {dosyaAdı = dosyaAdı.Yedek (c,' - '); }
jcollum

8
Bu çözüm ad çatışmalarını nasıl ele alır? Görünüşe göre birden fazla dizge tek bir dosya adıyla eşleşebilir (örneğin "Cehennem?" Ve "Cehennem *"). Sadece rahatsız edici karakterleri kaldırıyorsanız sorun yok; aksi takdirde isim uyuşmazlıkları konusunda dikkatli olmanız gerekir.
Stefano Ricciardi

2
dosya sisteminin ad (ve yol) uzunluğu sınırları ne olacak? ayrılmış dosya adları (PRN CON) ne olacak? Verileri ve orijinal adı saklamanız gerekiyorsa, Guid adlarına sahip 2 dosya kullanabilirsiniz: guid.txt ve guid.dat
Jack

7
Tek satırlık, eğlenceli sonuç için = Path.GetInvalidFileNameChars (). Aggregate (sonuç, (geçerli, c) => geçerli.Değiştir (c, '-'));
Paul Knopf

1
@PaulKnopf, JetBrain'in bu kod için telif hakkı olmadığından emin misiniz;)
Marcus

37

Geçersiz karakterleri çıkarmak için:

static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars();

// Builds a string out of valid chars
var validFilename = new string(filename.Where(ch => !invalidFileNameChars.Contains(ch)).ToArray());

Geçersiz karakterleri değiştirmek için:

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

Geçersiz karakterleri değiştirmek (ve Hell * - Hell $ gibi olası ad çakışmasını önlemek) için:

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

34

Bu soru sorulduğunda olmuştur birçok kez önce , önce defalarca belirttiği gibi, ve IO.Path.GetInvalidFileNameCharsyeterli değildir.

İlk olarak, PRN ve CON gibi ayrılmış ve dosya adlarına izin verilmeyen birçok ad vardır. Yalnızca kök klasörde izin verilmeyen başka adlar da vardır. Bir dönemle biten isimlere de izin verilmez.

İkincisi, çeşitli uzunluk sınırlamaları vardır. NTFS'nin tam listesini buradan okuyun .

Üçüncüsü, başka sınırlamaları olan dosya sistemlerine iliştirebilirsiniz. Örneğin, ISO 9660 dosya adları "-" ile başlayamaz ancak içerebilir.

Dördüncüsü, iki süreç "keyfi olarak" aynı adı seçerse ne yaparsınız?

Genel olarak, dosya adları için harici olarak oluşturulmuş adlar kullanmak kötü bir fikirdir. Kendi özel dosya adlarınızı oluşturmanızı ve insan tarafından okunabilir adları dahili olarak saklamanızı öneririm.


13
Teknik olarak doğru olmanıza rağmen, GetInvalidFileNameChars, onu kullanacağınız durumların% 80'i için iyidir, bu nedenle iyi bir cevaptır. Cevabınız, sanırım kabul edilen cevaba bir yorum olarak daha uygun olurdu.
CubanX

4
DourHighArch'a katılıyorum. Dosyayı bir kılavuz olarak dahili olarak kaydedin, bir veritabanında depolanan "kolay ad" a referans yapın. Kullanıcıların web sitesindeki yollarınızı kontrol etmesine izin vermeyin, aksi takdirde web.config'inizi çalmaya çalışırlar. Temizlemek için url yeniden yazmayı dahil ederseniz, yalnızca veritabanındaki eşleşen uygun url'ler için çalışır.
rtpHarry

22

Grauenwolf'a katılıyorum ve kesinlikle tavsiye ederim Path.GetInvalidFileNameChars()

İşte benim C # katkım:

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

ps - bu olması gerekenden daha şifreli - özlü olmaya çalışıyordum.


3
Neden dünyada olur kullandığınız Array.ForEachsadece yerine foreachburada
BlueRaja - Dany Pflughoeft

9
Daha da özlü / şifreli olmak istersen:Path.GetInvalidFileNameChars().Aggregate(file, (current, c) => current.Replace(c, '-'))
Michael Petito

@ BlueRaja-DannyPflughoeft Çünkü daha yavaş yapmak istiyorsun?
Jonathan Allen

@Johnathan Allen, foreach'in Array.ForEach'den daha hızlı olduğunu düşündüren nedir?
Ryan Buddicom

5
@rbuddicom Array.ForEach bir delege alır, bu da onun satır içi olmayan bir işlevi çağırması gerektiği anlamına gelir. Kısa dizeler için, işlev çağrısı ek yüküne gerçek mantıktan daha fazla zaman harcayabilirsiniz. .NET Core, ek yükü azaltarak çağrıları "sanallaştırmayı kaldırmanın" yollarını arıyor.
Jonathan Allen

13

İşte benim versiyonum:

static string GetSafeFileName(string name, char replace = '_') {
  char[] invalids = Path.GetInvalidFileNameChars();
  return new string(name.Select(c => invalids.Contains(c) ? replace : c).ToArray());
}

GetInvalidFileNameChars'ın sonucunun nasıl hesaplandığından emin değilim, ancak "Al" bunun önemsiz olmadığını gösteriyor, bu yüzden sonuçları önbelleğe alıyorum. Dahası, bu, giriş dizesini birden çok kez değil, yalnızca bir kez dolaşır, yukarıdaki çözümlerde olduğu gibi geçersiz karakterler kümesi üzerinde yinelenir ve bunları kaynak dizesinde birer birer değiştirir. Ayrıca, Nereye dayalı çözümleri seviyorum, ancak geçersiz karakterleri kaldırmak yerine değiştirmeyi tercih ediyorum. Son olarak, dizge üzerinde yinelediğimde karakterleri dizelere dönüştürmekten kaçınmak için değiştireceğim tam olarak bir karakterdir.

Tüm bunları profilleme yapmadan söylüyorum - bu sadece bana "iyi hissettirdi". :)


1
new HashSet<char>(Path.GetInvalidFileNameChars())O (n) numaralandırmadan kaçınmak için yapabilirsiniz - mikro optimizasyon.
TrueWill

12

İşte şimdi kullandığım işlev (C # örneği için jcollum'a teşekkürler):

public static string MakeSafeFilename(string filename, char replaceChar)
{
    foreach (char c in System.IO.Path.GetInvalidFileNameChars())
    {
        filename = filename.Replace(c, replaceChar);
    }
    return filename;
}

Ben sadece kolaylık olması için bunu "Helpers" sınıfına koydum.


7

Dosya adları için bazen daha kullanıcı tarafından okunabilen tüm özel karakterleri hızlı bir şekilde çıkarmak istiyorsanız, bu iyi çalışır:

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"

1
aslında \Walfa sayısal olmayanlardan ( [^A-Za-z0-9_]) daha fazlasıyla eşleşir . Tüm Unicode 'kelime' karakterleri de (русский 中文 ..., vb.) Değiştirilmeyecektir. Ama bu iyi bir şey.
Ishmael

Tek dezavantajı, bunun da kaldırılmasıdır, bu .nedenle önce uzantıyı çıkarmanız ve sonra tekrar eklemeniz gerekir.
korku

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

5

Neden dizeyi aşağıdaki gibi Base64 eşdeğerine dönüştürmüyorsunuz:

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

Okuyabilmek için geri dönüştürmek istiyorsanız:

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

Bunu, PNG dosyalarını rastgele bir açıklamadan benzersiz bir adla kaydetmek için kullandım.


5

İşte ClipFlair'in ( http://github.com/Zoomicon/ClipFlair ) StringExtensions statik sınıfına (Utils.Silverlight projesi), yukarıda Dour High Arch tarafından yayınlanan ilgili stackoverflow sorularına bağlantılardan toplanan bilgilere dayanarak ekledim:

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

2
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;            
}

1

Bunu hızlı ve anlaşılması kolay buluyorum:

<Extension()>
Public Function MakeSafeFileName(FileName As String) As String
    Return FileName.Where(Function(x) Not IO.Path.GetInvalidFileNameChars.Contains(x)).ToArray
End Function

Bir nedeni çalışır stringolduğu IEnumerablebir şekilde chardiziye ve orada stringbir sürer kurucu dizesi chardizisi.


1

Eski projelerimden, 2 yıldır mükemmel çalışan bu çözümü buldum. Kuraldışı karakterleri "!" İle değiştiriyorum ve ardından çift !! 'i kontrol ediyorum, kendi karakterinizi kullanın.

    public string GetSafeFilename(string filename)
    {
        string res = string.Join("!", filename.Split(Path.GetInvalidFileNameChars()));

        while (res.IndexOf("!!") >= 0)
            res = res.Replace("!!", "!");

        return res;
    }

0

Birçok cevap kullanmayı öneriyor Path.GetInvalidFileNameChars() bana kötü bir çözüm gibi görünen . Kara listeye almak yerine beyaz listeyi kullanmanızı tavsiye ederim çünkü bilgisayar korsanları her zaman sonunda onu atlamanın bir yolunu bulacaktır.

İşte kullanabileceğiniz bir kod örneği:

    string whitelist = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
    foreach (char c in filename)
    {
        if (!whitelist.Contains(c))
        {
            filename = filename.Replace(c, '-');
        }
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.