Yol ve dosya adlarından yasadışı karakterler nasıl kaldırılır?


456

Basit bir dizeden yasadışı yol ve dosya karakterlerini kaldırmak için sağlam ve basit bir yol gerekir. Aşağıdaki kodu kullandım ama hiçbir şey yapmıyor gibi görünüyor, ne eksik?

using System;
using System.IO;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string illegal = "\"M<>\"\\a/ry/ h**ad:>> a\\/:*?\"<>| li*tt|le|| la\"mb.?";

            illegal = illegal.Trim(Path.GetInvalidFileNameChars());
            illegal = illegal.Trim(Path.GetInvalidPathChars());

            Console.WriteLine(illegal);
            Console.ReadLine();
        }
    }
}

1
Kırp, karakterleri dizelerin başından ve sonundan kaldırır. Ancak, muhtemelen verilerin neden geçersiz olduğunu sormalısınız ve verileri denemek ve sterilize etmek / düzeltmek yerine verileri reddedin.
user7116

8
Unix stil adları Windows'ta geçerli değil ve 8.3 kısa adlarıyla uğraşmak istemiyorum.
Gary Willoughby

GetInvalidFileNameChars()klasör yollarından: \ etc gibi şeyler çıkarır.
CAD bloke

1
Path.GetInvalidPathChars()şerit gibi görünmüyor *ya da?
CAD bloke

19
Bu sorunun beş cevabını test ettim (100.000 zamanlı döngü) ve aşağıdaki yöntem en hızlısıdır. Normal ifade 2. sırada yer aldı ve% 25 daha yavaştı: genel string GetSafeFilename (string dosyaadı) {return string.Join ("_", dosyaadı.Split (Path.GetInvalidFileNameChars ())); }
Brain2000

Yanıtlar:


494

Bunun yerine böyle bir şey deneyin;

string illegal = "\"M\"\\a/ry/ h**ad:>> a\\/:*?\"| li*tt|le|| la\"mb.?";
string invalid = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());

foreach (char c in invalid)
{
    illegal = illegal.Replace(c.ToString(), ""); 
}

Ama yorumlara katılıyorum, yasadışı bir yolu meşru ama muhtemelen istenmeyen bir yola dönüştürmek yerine, muhtemelen yasadışı yolların kaynağı ile uğraşmaya çalışacağım.

Düzenleme: Veya Regex kullanarak potansiyel olarak 'daha iyi' bir çözüm.

string illegal = "\"M\"\\a/ry/ h**ad:>> a\\/:*?\"| li*tt|le|| la\"mb.?";
string regexSearch = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
Regex r = new Regex(string.Format("[{0}]", Regex.Escape(regexSearch)));
illegal = r.Replace(illegal, "");

Yine de, soru ilk başta neden yaptığınızı sormaya yalvarmaktadır.


40
İki listeyi birlikte eklemek gerekli değildir. Geçersiz dosya adı karakter listesi geçersiz yol karakter listesi içeriyor ve birkaç tane daha var. Burada her iki listenin listesi int: 34,60,62,124,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,58,42,63,92,47 34,60,62,124,0,1,2 , 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27 , 28,29,30,31
Sarel Botha

9
@sjbotha bu Windows ve Microsoft'un .NET uygulaması için geçerli olabilir mono çalışan Linux için aynı varsayımı yapmaya hazır değilim.
Matthew Scharley

7
İlk çözüm ile ilgili. Bir StringBuilder, dize atamalarından daha verimli olmamalı mı?
epignosisx

6
Değeri için, @MatthewScharley, GetInvalidPathChars () 'in Mono uygulaması yalnızca 0x00, GetInvalidFileNameChars () ise Windows olmayan platformlarda çalışırken yalnızca 0x00 ve' / 'döndürür. Windows'ta, geçersiz karakter listeleri çok daha uzundur ve GetInvalidPathChars () tamamen GetInvalidFileNameChars () içinde çoğaltılmıştır. Bu, yakın gelecekte değişmeyecek, bu yüzden gerçekten yaptığınız tek şey bu işlevin çalışması için gereken süreyi iki katına çıkarmak çünkü geçerli bir yol tanımının yakında değişeceğinden endişe ediyorsunuz. Hangi olmayacak.
Warren Rumak

13
@Charleh bu tartışma çok gereksiz ... kod her zaman optimize edilmelidir ve bunun yanlış olma riski yoktur. Dosya adı da yolun bir parçasıdır. Bu yüzden, GetInvalidPathChars()içermeyen karakterleri içermesi mantıksızdır GetInvalidFileNameChars(). "Erken" optimizasyon üzerinde doğruluk almıyorsunuz. Sadece kötü kod kullanıyorsunuz.
Stefan Fabian

357

Orijinal soru "geçersiz karakterleri kaldırmak" istedi:

public string RemoveInvalidChars(string filename)
{
    return string.Concat(filename.Split(Path.GetInvalidFileNameChars()));
}

Bunun yerine bunları değiştirmek isteyebilirsiniz:

public string ReplaceInvalidChars(string filename)
{
    return string.Join("_", filename.Split(Path.GetInvalidFileNameChars()));    
}

Bu cevap Ceres tarafından başka bir iş parçacığında oldu , ben gerçekten düzgün ve basit gibi.


10
OP'nin sorusunu tam olarak cevaplamak için, "_" yerine "" kullanmanız gerekir, ancak cevabınız muhtemelen daha çok uygulamada geçerlidir. Bence yasadışı karakterleri yasal bir karakterle değiştirmek daha yaygın olarak yapılır.
BH

38
Bu sorudan (100.000 zamanlamalı döngü) beş yöntemi test ettim ve bu yöntem en hızlı olanı. Normal ifade 2. sırada yer aldı ve bu yöntemden% 25 daha yavaştı.
Brain2000

10
@BH'nin yorumuna hitap etmek için, string.Concat (name.Split (Path.GetInvalidFileNameChars ()))
Michael Sutton

210

Dosya adlarını temizlemek için Linq kullanıyorum. Geçerli yolları kontrol etmek için bunu kolayca genişletebilirsiniz.

private static string CleanFileName(string fileName)
{
    return Path.GetInvalidFileNameChars().Aggregate(fileName, (current, c) => current.Replace(c.ToString(), string.Empty));
}

Güncelleme

Bazı yorumlar bu yöntemin onlar için çalışmadığını gösterir, bu nedenle yöntemi doğrulayabilmeniz için bir DotNetFiddle snippet'ine bir bağlantı ekledim.

https://dotnetfiddle.net/nw1SWY


4
Bu benim için işe yaramadı. Yöntem temiz dize döndürmüyor. Aktarılan dosya adını olduğu gibi döndürüyor.
Karan

@Karan ne dedi, bu işe yaramıyor, orijinal dize geri geliyor.
Jon

Aslında bu hoşuma da Linq ile bunu yapabilirsiniz: var invalid = new HashSet<char>(Path.GetInvalidPathChars()); return new string(originalString.Where(s => !invalid.Contains(s)).ToArray()). Performans muhtemelen harika değil ama muhtemelen önemli değil.
Casey

2
@Karan veya Jon Bu işlevi hangi girdiye gönderiyorsunuz? Bu yöntemin doğrulanması için düzenlememe bakın.
Michael Minton

3
Kolay - çocuklar geçerli karakterlerle dizeleri geçiyorlardı. Serin Aggregate çözümü için seçildi.
Nickmaovich

89

Linq kullanarak yasadışı karakterleri aşağıdaki gibi kaldırabilirsiniz:

var invalidChars = Path.GetInvalidFileNameChars();

var invalidCharsRemoved = stringWithInvalidChars
.Where(x => !invalidChars.Contains(x))
.ToArray();

DÜZENLE
yorumlarda belirtilen gerekli düzenleme ile şu şekilde görünür:

var invalidChars = Path.GetInvalidFileNameChars();

string invalidCharsRemoved = new string(stringWithInvalidChars
  .Where(x => !invalidChars.Contains(x))
  .ToArray());

1
Ben bu şekilde seviyorum: sadece izin verilen karakter dizesinde (ki char dizisinden başka bir şey değildir) tutmak.
Dude Pascalou

6
Bunun eski bir soru olduğunu biliyorum, ama bu harika bir cevap. Ancak, ben bir dize yapıcı bırakmanız gerekir böylece dolaylı veya açıkça (deli, biliyorum) dize char [] döküm olamaz eklemek c # eklemek istedim.
JNYRanger

1
Bunu onaylamadım, ancak Path.GetInvalidPathChars () 'in GetInvalidFileNameChars ()' ın bir üst kümesi olmasını ve hem dosya adlarını hem de yolları kapsamasını bekliyorum, bu yüzden muhtemelen bunun yerine kullanacağım.
angularsen

3
@anjdreas aslında Path.GetInvalidPathChars (), başka bir yol değil, Path.GetInvalidFileNameChars () öğesinin bir alt kümesi gibi görünüyor. Örneğin Path.GetInvalidPathChars () '?' Döndürmez.
Rafael Costa

1
Bu iyi bir cevap. Hem dosya adı listesini hem de dosya yolu listesini kullanıyorum: ____________________________ string cleanData = new string (data.Where (x =>! Path.GetInvalidFileNameChars (). İçerir (x) &&! Path.GetInvalidPathChars (). İçerir (x)). Sıralamak());
goamn

27

Bunların hepsi harika çözümlerdir, ancak hepsine güvenir Path.GetInvalidFileNameChars, ki bu düşündüğünüz kadar güvenilir olmayabilir. Aşağıdaki MSDN belgelerinde aşağıdaki açıklamaya dikkat edin Path.GetInvalidFileNameChars:

Bu yöntemden döndürülen dizinin , dosya ve dizin adlarında geçersiz olan tüm karakter kümesini içereceği garanti edilmez. Geçersiz karakterlerin tamamı dosya sistemine göre değişebilir. Örneğin, Windows tabanlı masaüstü platformlarında geçersiz yol karakterleri, 1'den 31'e kadar ASCII / Unicode karakterlerin yanı sıra (<), (<), (>), pipe (|), backspace ( \ b), null (\ 0) ve sekme (\ t).

Path.GetInvalidPathCharsYöntem ile daha iyi değil . Aynı ifadeyi içerir.


13
O zaman Path.GetInvalidFileNameChars'ın amacı nedir? Hangi dosya sistemi üzerinde çalıştığımı bilmek ve bana uygun geçersiz karakterleri sunmak için .NET dayanarak, geçerli sistem için tam olarak geçersiz karakterler döndürmesini beklenir. Bu durumda değilse ve ilk başta güvenilir olmayan sabit kodlanmış karakterler döndürürse, sıfır değerine sahip olduğu için bu yöntemin kaldırılması gerekir.
Ocak

1
Bunun eski bir yorum olduğunu biliyorum, ama @Jan başka bir dosya sistemine yazmak isteyebilirsiniz, belki bu yüzden bir uyarı vardır.
fantastik78

3
@ fantastik78 iyi bir nokta, ama bu durumda uzak FS'mi belirtmek için ek bir enum argümanına sahip olmak isterdim. Bu çok fazla bakım çabasıysa (büyük olasılıkla), tüm bu yöntem hala kötü bir fikirdir, çünkü size yanlış güvenlik izlenimi verir.
Ocak

1
@ Sana tamamen katılıyorum, sadece uyarıyı tartışıyordum.
fantastik78

İlginçtir ki bu bir tür "kara listeleme" geçersiz karakteridir. Burada sadece bilinen geçerli karakterleri "beyaz listeye almak" daha iyi olmaz mıydı ?! İzin verilen uygulamaları beyaz listeye eklemek yerine aptal "virusscanner" fikrini hatırlatıyor ....
Bernhard

26

Dosya adları için:

var cleanFileName = string.Join("", fileName.Split(Path.GetInvalidFileNameChars()));

Tam yollar için:

var cleanPath = string.Join("", path.Split(Path.GetInvalidPathChars()));

Bunu bir güvenlik özelliği olarak kullanmayı düşünüyorsanız, daha sağlam bir yaklaşım tüm yolları genişletmek ve daha sonra kullanıcı tarafından sağlanan yolun gerçekten kullanıcının erişmesi gereken bir dizinin alt öğesi olduğunu doğrulamak olacaktır.


18

Yeni başlayanlar için, Kırp yalnızca karakterleri dizenin başından veya sonundan kaldırır . İkinci olarak, rahatsız edici karakterleri gerçekten kaldırmak isteyip istemediğinizi veya hızlı bir şekilde başarısız olup olmadığınızı değerlendirmeli ve kullanıcıya dosya adının geçersiz olduğunu bildirmelisiniz. Seçimim ikincisidir, ancak cevabım en azından işleri doğru ve yanlış şekilde nasıl yapacağınızı göstermelidir:

Belirli bir dizenin geçerli bir dosya adı olup olmadığını nasıl kontrol edeceğinizi gösteren StackOverflow sorusu . Normal bir ifade değiştirme ile karakterleri kaldırmak için bu sorudaki normal ifadeyi kullanabileceğinizi unutmayın (gerçekten yapmanız gerekiyorsa).


Özellikle ikinci tavsiyeye katılıyorum.
OregonGhost

4
Normalde ikinciye katılırdım, ancak dosya adı oluşturan ve bazı durumlarda yasadışı karakterler içerebilen bir programım var. Yana benim program yasadışı dosya oluşturuyor, ben o karakterleri değiştirmek / kaldırmak için uygun olduğunu düşünüyorum. (Sadece geçerli bir kullanım durumunu işaret ediyor)
JDB hala Monica'yı

16

Kullanıcı girdisinden yasadışı karakteri kaldırmanın en iyi yolu, Regex sınıfını kullanarak yasadışı karakteri değiştirmek, kodda yöntem oluşturmak veya RegularExpression denetimini kullanarak istemci tarafında doğrulamaktır.

public string RemoveSpecialCharacters(string str)
{
    return Regex.Replace(str, "[^a-zA-Z0-9_]+", "_", RegexOptions.Compiled);
}

VEYA

<asp:RegularExpressionValidator ID="regxFolderName" 
                                runat="server" 
                                ErrorMessage="Enter folder name with  a-z A-Z0-9_" 
                                ControlToValidate="txtFolderName" 
                                Display="Dynamic" 
                                ValidationExpression="^[a-zA-Z0-9_]*$" 
                                ForeColor="Red">

5
IMHO bu çözüm diğerlerinden çok daha iyidir Tüm geçersiz karakterleri aramak yerine hangisinin geçerli olduğunu tanımlayın.
igorushi

15

Bunu başarmak için düzenli ifadeler kullanıyorum. İlk olarak, regex dinamik olarak oluşturmak.

string regex = string.Format(
                   "[{0}]",
                   Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);

Sonra sadece bulmak ve değiştirmek için removeInvalidChars.Replace çağırıyorum. Bu açık bir şekilde yol karakterlerini de kapsayacak şekilde genişletilebilir.


Garip, benim için çalışıyor. Şansım olduğunda tekrar kontrol edeceğim. Daha spesifik olabilir ve sizin için tam olarak neyin işe yaramadığını açıklayabilir misiniz?
Jeff Yates

1
(En azından düzgün şekilde) çalışmaz çünkü yol karakterlerinden düzgün bir şekilde kaçmazsınız ve bazılarının özel bir anlamı vardır. Bunun nasıl yapılacağı için cevabıma bakın.
Matthew Scharley

@Jeff: Sürümünüz, biraz değiştirirseniz, Matthew'den daha iyidir. Nasıl yapılacağına dair cevabım.
Ocak

2
Ayrıca MSDN'de bulunabilecek bazı geçersiz dosya adı desenleri ekler ve çözümünüzü aşağıdaki normal new Regex(String.Format("^(CON|PRN|AUX|NUL|CLOCK\$|COM[1-9]|LPT[1-9])(?=\..|$)|(^(\.+|\s+)$)|((\.+|\s+)$)|([{0}])", Regex.Escape(new String(Path.GetInvalidFileNameChars()))), RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.CultureInvariant);
ifadeye

13

Jeff Yates fikrini kesinlikle tercih ediyorum. Biraz değiştirirseniz mükemmel çalışır:

string regex = String.Format("[{0}]", Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);

Bu iyileştirme sadece otomatik olarak oluşturulan normal ifadeden kaçmaktır.


11

İşte .NET 3 ve üstü için yardımcı olması gereken bir kod snippet'i.

using System.IO;
using System.Text.RegularExpressions;

public static class PathValidation
{
    private static string pathValidatorExpression = "^[^" + string.Join("", Array.ConvertAll(Path.GetInvalidPathChars(), x => Regex.Escape(x.ToString()))) + "]+$";
    private static Regex pathValidator = new Regex(pathValidatorExpression, RegexOptions.Compiled);

    private static string fileNameValidatorExpression = "^[^" + string.Join("", Array.ConvertAll(Path.GetInvalidFileNameChars(), x => Regex.Escape(x.ToString()))) + "]+$";
    private static Regex fileNameValidator = new Regex(fileNameValidatorExpression, RegexOptions.Compiled);

    private static string pathCleanerExpression = "[" + string.Join("", Array.ConvertAll(Path.GetInvalidPathChars(), x => Regex.Escape(x.ToString()))) + "]";
    private static Regex pathCleaner = new Regex(pathCleanerExpression, RegexOptions.Compiled);

    private static string fileNameCleanerExpression = "[" + string.Join("", Array.ConvertAll(Path.GetInvalidFileNameChars(), x => Regex.Escape(x.ToString()))) + "]";
    private static Regex fileNameCleaner = new Regex(fileNameCleanerExpression, RegexOptions.Compiled);

    public static bool ValidatePath(string path)
    {
        return pathValidator.IsMatch(path);
    }

    public static bool ValidateFileName(string fileName)
    {
        return fileNameValidator.IsMatch(fileName);
    }

    public static string CleanPath(string path)
    {
        return pathCleaner.Replace(path, "");
    }

    public static string CleanFileName(string fileName)
    {
        return fileNameCleaner.Replace(fileName, "");
    }
}

8

Yukarıdaki çözümlerin çoğu, hem yol hem de dosya adı için yasadışı karakterleri birleştirir ve bu da yanlıştır (her iki çağrı da şu anda aynı karakter kümesini döndürse bile). Önce path + dosya adını yola ve dosya adına bölerdim, sonra uygun kümeyi ya varsa uygular, sonra ikisini tekrar birleştiririm.

wvd_vegt


+1: Çok doğru. Bugün, .NET 4.0'da çalışan, en üstteki yanıttan regex çözümü, tüm ters eğik çizgileri tam yoldan çekti. Bu yüzden dir yolu için bir regex ve sadece dosya adı için bir regex yaptım, ayrı olarak temizlenir ve yeniden birleştirilir
dario_ramos

Bu doğru olabilir ama bu soruya cevap vermiyor. Zaten 'Bunu böyle yapardım' meselesinin zaten burada bulunan bazı çözümlere kıyasla çok yararlı olduğundan emin değilim (örneğin aşağıdaki Lilly'nin cevabına bakınız)
Ian Grainger

6

Geçersiz karakterleri tek bir karakterle kaldırır veya değiştirirseniz, çakışmalar olabilir:

<abc -> abc
>abc -> abc

Bunu önlemek için basit bir yöntem:

public static string ReplaceInvalidFileNameChars(string s)
{
    char[] invalidFileNameChars = System.IO.Path.GetInvalidFileNameChars();
    foreach (char c in invalidFileNameChars)
        s = s.Replace(c.ToString(), "[" + Array.IndexOf(invalidFileNameChars, c) + "]");
    return s;
}

Sonuç:

 <abc -> [1]abc
 >abc -> [2]abc

5

Bir istisna atın.

if ( fileName.IndexOfAny(Path.GetInvalidFileNameChars()) > -1 )
            {
                throw new ArgumentException();
            }

4

Bu canavarı eğlence için yazdım, size gidiş-dönüş sağlar:

public static class FileUtility
{
    private const char PrefixChar = '%';
    private static readonly int MaxLength;
    private static readonly Dictionary<char,char[]> Illegals;
    static FileUtility()
    {
        List<char> illegal = new List<char> { PrefixChar };
        illegal.AddRange(Path.GetInvalidFileNameChars());
        MaxLength = illegal.Select(x => ((int)x).ToString().Length).Max();
        Illegals = illegal.ToDictionary(x => x, x => ((int)x).ToString("D" + MaxLength).ToCharArray());
    }

    public static string FilenameEncode(string s)
    {
        var builder = new StringBuilder();
        char[] replacement;
        using (var reader = new StringReader(s))
        {
            while (true)
            {
                int read = reader.Read();
                if (read == -1)
                    break;
                char c = (char)read;
                if(Illegals.TryGetValue(c,out replacement))
                {
                    builder.Append(PrefixChar);
                    builder.Append(replacement);
                }
                else
                {
                    builder.Append(c);
                }
            }
        }
        return builder.ToString();
    }

    public static string FilenameDecode(string s)
    {
        var builder = new StringBuilder();
        char[] buffer = new char[MaxLength];
        using (var reader = new StringReader(s))
        {
            while (true)
            {
                int read = reader.Read();
                if (read == -1)
                    break;
                char c = (char)read;
                if (c == PrefixChar)
                {
                    reader.Read(buffer, 0, MaxLength);
                    var encoded =(char) ParseCharArray(buffer);
                    builder.Append(encoded);
                }
                else
                {
                    builder.Append(c);
                }
            }
        }
        return builder.ToString();
    }

    public static int ParseCharArray(char[] buffer)
    {
        int result = 0;
        foreach (char t in buffer)
        {
            int digit = t - '0';
            if ((digit < 0) || (digit > 9))
            {
                throw new ArgumentException("Input string was not in the correct format");
            }
            result *= 10;
            result += digit;
        }
        return result;
    }
}

1
Bunu seviyorum çünkü aynı sonuç yolunu oluşturan iki farklı dizeye sahip olmaktan kaçınır.
Kim

3

Tüm kötü karakterleri kontrol etmeye çalışmak yerine, normal ifade kullanarak ve hangi karakterlere izin verildiğini belirterek doğrulamanın çok daha kolay olduğunu düşünüyorum. Bu bağlantılara bakın: http://www.c-sharpcorner.com/UploadFile/prasad_1/RegExpPSD12062005021717AM/RegExpPSD.aspx http://www.windowsdevcenter.com/pub/a/oreilly/windows/news/csharp_0101.html

Ayrıca, "düzenli ifade editörleri" için bir arama yapmak, onlar çok yardımcı olur. Hatta bazı kodlar bile c # çıktı sizin için vardır.


.Net, programların birden fazla platformda (örn. Linux / Unix ve Windows gibi) çalışmasına izin vermeyi amaçlayan bir çerçeve olduğu göz önüne alındığında, Path.GetInvalidFileNameChars () 'in en iyi şey olduğunu ve ne olduğunu bildiğini içerdiğini hissediyorum. t Programınızın çalıştırıldığı dosya sistemi için geçerlidir. Programınız Linux'ta asla çalışmayacak olsa bile (belki WPF koduyla doludur), her zaman bazı yeni Windows dosya sistemlerinin gelecekte ortaya çıkma ve farklı geçerli / geçersiz karakterlere sahip olma şansı vardır. Kendi regex ile haddeleme tekerleği yeniden icat ve bir platform sorunu kendi kodunuza kaydırmak.
Daniel Scott

Yine de çevrimiçi regex editörleri / testçileri hakkındaki tavsiyelerinize katılıyorum. Onları paha biçilmez buluyorum (regex'ler zor şeyler ve sizi kolayca kurtarabilecek inceliklerle dolu, kenar vakalarıyla çılgınca beklenmedik bir şekilde davranan bir regex veriyor). En sevdiğim regex101.com ( Regex'i nasıl bozduğunu ve maç için ne beklediğini açıkça gösteriyor). Ben de maç grupları ve karakter sınıfları kompakt bir görsel temsili var gibi debuggex.com gibi.
Daniel Scott

3

Bu O (n) gibi görünüyor ve dizelere çok fazla bellek harcamıyor:

    private static readonly HashSet<char> invalidFileNameChars = new HashSet<char>(Path.GetInvalidFileNameChars());

    public static string RemoveInvalidFileNameChars(string name)
    {
        if (!name.Any(c => invalidFileNameChars.Contains(c))) {
            return name;
        }

        return new string(name.Where(c => !invalidFileNameChars.Contains(c)).ToArray());
    }

1
'Herhangi' işlevini kullandığınızda O (n) olduğunu sanmıyorum.
II OKLAR

@IIARROWS ve sizce ne?
Alexey F

Bilmiyorum, yorumumu yazarken öyle hissetmedim ... şimdi hesaplamaya çalıştım, haklı görünüyorsun.
II OKLAR

Performans değerlendirmeniz nedeniyle bunu seçtim. Teşekkürler.
Berend Engelbrecht

3

Buradaki cevapları tarayarak, hepsi ** geçersiz dosya adı karakterlerinin char dizisini kullanmayı içeriyor gibi görünüyor.

Kabul edilirse, bu mikro optimizasyon olabilir - ancak geçerli dosya adları olarak çok sayıda değeri kontrol etmek isteyen herkesin yararına, geçersiz karakterlerin bir karma kümesini oluşturmanın önemli ölçüde daha iyi performans getireceğini belirtmek gerekir.

Geçmişte bir hashset (veya sözlük) bir liste üzerinden yineleme ne kadar hızlı performans çok şaşırdım (şok). Dizelerle gülünç derecede düşük bir sayıdır (bellekten yaklaşık 5-7 öğe). Diğer basit verilerle (nesne referansları, sayılar vb.) Sihirli geçiş yaklaşık 20 öğedir.

Path.InvalidFileNameChars "listesinde" 40 geçersiz karakter var. Bugün bir arama yaptı ve burada StackOverflow'da 40 öğenin bir dizinin / listenin yarısından biraz daha uzun süreceğini gösteren oldukça iyi bir kıyaslama var: https://stackoverflow.com/a/10762995/949129

İşte yolları sterilize etmek için kullandığım yardımcı sınıf. Şimdi neden fantezi değiştirme seçeneği olduğunu unuttum, ama orada şirin bir bonus olarak.

Ek bonus yöntemi "IsValidLocalPath" da :)

(** düzenli ifadeler kullanmayanlar)

public static class PathExtensions
{
    private static HashSet<char> _invalidFilenameChars;
    private static HashSet<char> InvalidFilenameChars
    {
        get { return _invalidFilenameChars ?? (_invalidFilenameChars = new HashSet<char>(Path.GetInvalidFileNameChars())); }
    }


    /// <summary>Replaces characters in <c>text</c> that are not allowed in file names with the 
    /// specified replacement character.</summary>
    /// <param name="text">Text to make into a valid filename. The same string is returned if 
    /// it is valid already.</param>
    /// <param name="replacement">Replacement character, or NULL to remove bad characters.</param>
    /// <param name="fancyReplacements">TRUE to replace quotes and slashes with the non-ASCII characters ” and ⁄.</param>
    /// <returns>A string that can be used as a filename. If the output string would otherwise be empty, "_" is returned.</returns>
    public static string ToValidFilename(this string text, char? replacement = '_', bool fancyReplacements = false)
    {
        StringBuilder sb = new StringBuilder(text.Length);
        HashSet<char> invalids = InvalidFilenameChars;
        bool changed = false;

        for (int i = 0; i < text.Length; i++)
        {
            char c = text[i];
            if (invalids.Contains(c))
            {
                changed = true;
                char repl = replacement ?? '\0';
                if (fancyReplacements)
                {
                    if (c == '"') repl = '”'; // U+201D right double quotation mark
                    else if (c == '\'') repl = '’'; // U+2019 right single quotation mark
                    else if (c == '/') repl = '⁄'; // U+2044 fraction slash
                }
                if (repl != '\0')
                    sb.Append(repl);
            }
            else
                sb.Append(c);
        }

        if (sb.Length == 0)
            return "_";

        return changed ? sb.ToString() : text;
    }


    /// <summary>
    /// Returns TRUE if the specified path is a valid, local filesystem path.
    /// </summary>
    /// <param name="pathString"></param>
    /// <returns></returns>
    public static bool IsValidLocalPath(this string pathString)
    {
        // From solution at https://stackoverflow.com/a/11636052/949129
        Uri pathUri;
        Boolean isValidUri = Uri.TryCreate(pathString, UriKind.Absolute, out pathUri);
        return isValidUri && pathUri != null && pathUri.IsLoopback;
    }
}

2
public static class StringExtensions
      {
        public static string RemoveUnnecessary(this string source)
        {
            string result = string.Empty;
            string regex = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
            Regex reg = new Regex(string.Format("[{0}]", Regex.Escape(regex)));
            result = reg.Replace(source, "");
            return result;
        }
    }

Yöntemi açıkça kullanabilirsiniz.


2

Dosya adı karakterleri içeremez Path.GetInvalidPathChars(), +ve #sembolleri ve diğer özel isimler. Tüm kontrolleri tek bir sınıfta birleştirdik:

public static class FileNameExtensions
{
    private static readonly Lazy<string[]> InvalidFileNameChars =
        new Lazy<string[]>(() => Path.GetInvalidPathChars()
            .Union(Path.GetInvalidFileNameChars()
            .Union(new[] { '+', '#' })).Select(c => c.ToString(CultureInfo.InvariantCulture)).ToArray());


    private static readonly HashSet<string> ProhibitedNames = new HashSet<string>
    {
        @"aux",
        @"con",
        @"clock$",
        @"nul",
        @"prn",

        @"com1",
        @"com2",
        @"com3",
        @"com4",
        @"com5",
        @"com6",
        @"com7",
        @"com8",
        @"com9",

        @"lpt1",
        @"lpt2",
        @"lpt3",
        @"lpt4",
        @"lpt5",
        @"lpt6",
        @"lpt7",
        @"lpt8",
        @"lpt9"
    };

    public static bool IsValidFileName(string fileName)
    {
        return !string.IsNullOrWhiteSpace(fileName)
            && fileName.All(o => !IsInvalidFileNameChar(o))
            && !IsProhibitedName(fileName);
    }

    public static bool IsProhibitedName(string fileName)
    {
        return ProhibitedNames.Contains(fileName.ToLower(CultureInfo.InvariantCulture));
    }

    private static string ReplaceInvalidFileNameSymbols([CanBeNull] this string value, string replacementValue)
    {
        if (value == null)
        {
            return null;
        }

        return InvalidFileNameChars.Value.Aggregate(new StringBuilder(value),
            (sb, currentChar) => sb.Replace(currentChar, replacementValue)).ToString();
    }

    public static bool IsInvalidFileNameChar(char value)
    {
        return InvalidFileNameChars.Value.Contains(value.ToString(CultureInfo.InvariantCulture));
    }

    public static string GetValidFileName([NotNull] this string value)
    {
        return GetValidFileName(value, @"_");
    }

    public static string GetValidFileName([NotNull] this string value, string replacementValue)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            throw new ArgumentException(@"value should be non empty", nameof(value));
        }

        if (IsProhibitedName(value))
        {
            return (string.IsNullOrWhiteSpace(replacementValue) ? @"_" : replacementValue) + value; 
        }

        return ReplaceInvalidFileNameSymbols(value, replacementValue);
    }

    public static string GetFileNameError(string fileName)
    {
        if (string.IsNullOrWhiteSpace(fileName))
        {
            return CommonResources.SelectReportNameError;
        }

        if (IsProhibitedName(fileName))
        {
            return CommonResources.FileNameIsProhibited;
        }

        var invalidChars = fileName.Where(IsInvalidFileNameChar).Distinct().ToArray();

        if(invalidChars.Length > 0)
        {
            return string.Format(CultureInfo.CurrentCulture,
                invalidChars.Length == 1 ? CommonResources.InvalidCharacter : CommonResources.InvalidCharacters,
                StringExtensions.JoinQuoted(@",", @"'", invalidChars.Select(c => c.ToString(CultureInfo.CurrentCulture))));
        }

        return string.Empty;
    }
}

Yöntem, GetValidFileNametüm yanlış verilerin yerine geçer _.


2

Windows dosya adlandırma için herhangi bir yasadışı karakterden dizeyi temizlemek için bir astar:

public static string CleanIllegalName(string p_testName) => new Regex(string.Format("[{0}]", Regex.Escape(new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars())))).Replace(p_testName, "");

1
public static bool IsValidFilename(string testName)
{
    return !new Regex("[" + Regex.Escape(new String(System.IO.Path.GetInvalidFileNameChars())) + "]").IsMatch(testName);
}

0

Bu istediğinizi isteyecek ve çarpışmalardan kaçınacaktır

 static string SanitiseFilename(string key)
    {
        var invalidChars = Path.GetInvalidFileNameChars();
        var sb = new StringBuilder();
        foreach (var c in key)
        {
            var invalidCharIndex = -1;
            for (var i = 0; i < invalidChars.Length; i++)
            {
                if (c == invalidChars[i])
                {
                    invalidCharIndex = i;
                }
            }
            if (invalidCharIndex > -1)
            {
                sb.Append("_").Append(invalidCharIndex);
                continue;
            }

            if (c == '_')
            {
                sb.Append("__");
                continue;
            }

            sb.Append(c);
        }
        return sb.ToString();

    }

0

Sanırım soru zaten tam olarak cevaplanmadı ... Cevaplar sadece temiz dosya adını VEYA yolu açıklıyor ... ikisini birden değil. İşte benim çözümüm:

private static string CleanPath(string path)
{
    string regexSearch = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
    Regex r = new Regex(string.Format("[{0}]", Regex.Escape(regexSearch)));
    List<string> split = path.Split('\\').ToList();
    string returnValue = split.Aggregate(string.Empty, (current, s) => current + (r.Replace(s, "") + @"\"));
    returnValue = returnValue.TrimEnd('\\');
    return returnValue;
}

0

Birkaç öneriyi birleştiren bir uzantı yöntemi oluşturdum:

  1. Karma kümesinde karma karakterleri tutma
  2. Path.GetInvalidFileNameChars yana ASCII 127. Aşağıdaki karakterleri filtreleyerek 0'dan 255 ASCII kodları ile mümkün tüm geçersiz karakterler içermeyen burada See ve MSDN
  3. Değiştirme karakterini tanımlama imkanı

Kaynak:

public static class FileNameCorrector
{
    private static HashSet<char> invalid = new HashSet<char>(Path.GetInvalidFileNameChars());

    public static string ToValidFileName(this string name, char replacement = '\0')
    {
        var builder = new StringBuilder();
        foreach (var cur in name)
        {
            if (cur > 31 && cur < 128 && !invalid.Contains(cur))
            {
                builder.Append(cur);
            }
            else if (replacement != '\0')
            {
                builder.Append(replacement);
            }
        }

        return builder.ToString();
    }
}

0

Bir dosya adındaki tüm yasadışı karakterleri yeni bir karakterle değiştiren bir işlev şunlardır:

public static string ReplaceIllegalFileChars(string FileNameWithoutPath, char ReplacementChar)
{
  const string IllegalFileChars = "*?/\\:<>|\"";
  StringBuilder sb = new StringBuilder(FileNameWithoutPath.Length);
  char c;

  for (int i = 0; i < FileNameWithoutPath.Length; i++)
  {
    c = FileNameWithoutPath[i];
    if (IllegalFileChars.IndexOf(c) >= 0)
    {
      c = ReplacementChar;
    }
    sb.Append(c);
  }
  return (sb.ToString());
}

Örneğin, alt çizgi yedek karakter olarak kullanılabilir:

NewFileName = ReplaceIllegalFileChars(FileName, '_');

Verdiğiniz yanıta ek olarak, lütfen bunun neden ve nasıl sorunu çözdüğüne dair kısa bir açıklama sağlayın.
jtate

-7

Veya sadece yapabilirsin

[YOUR STRING].Replace('\\', ' ').Replace('/', ' ').Replace('"', ' ').Replace('*', ' ').Replace(':', ' ').Replace('?', ' ').Replace('<', ' ').Replace('>', ' ').Replace('|', ' ').Trim();
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.