it-swarm-tr.com

Belirli bir dizenin Windows altında yasal/geçerli bir dosya adı olup olmadığını nasıl kontrol ederim?

Uygulamamda bir toplu iş dosyası yeniden adlandırma işlevselliği eklemek istiyorum. Bir kullanıcı bir hedef dosya adı modeli yazabilir ve (kalıptaki bazı joker karakterleri değiştirdikten sonra) Windows altında yasal bir dosya adı olup olmadığını kontrol etmem gerekir. [a-zA-Z0-9_]+ gibi normal ifadeler kullanmaya çalıştım, ancak çeşitli dillerden (ör. Umlautlar vb.) Pek çok ülkeye özgü karakter içermiyor. Böyle bir çek yapmanın en iyi yolu nedir?

150
tomash

Path.GetInvalidPathChars ve GetInvalidFileNameChars 'den geçersiz karakterlerin bir listesini alabilirsiniz.

UPD: Bkz. Steve Cooper'ın önerisi bunların düzenli bir ifadeyle nasıl kullanılacağına ilişkin.

UPD2: MSDN'deki Notlar bölümüne göre, "Bu yöntemden döndürülen dizinin, dosya ve dizin adlarında geçersiz tüm karakter kümesini içerme garantisi yoktur." Sixlettervaliables tarafından verilen cevap daha fazla ayrıntıya giriyor.

96
Eugene Katz

MSDN'den "Dosya veya Dizin Adlandırma" , burada Windows altında yasal bir dosya adının ne olduğuna ilişkin genel kurallardır:

Geçerli kod sayfasında (127'in üzerindeki Unicode/ANSI) herhangi bir karakteri kullanabilirsiniz:

  • <>:"/\|?* __
  • Tamsayı gösterimleri 0-31 olan karakterler (ASCII alanından az)
  • Hedef dosya sisteminin izin vermediği herhangi bir karakter (örneğin, takip eden süreleri veya boşlukları)
  • DOS adlarından herhangi biri: CON, PRN, AUX, NUL, COM0, COM1, COM2, COM3, COM4, ​​COM5, COM6, COM7, COM8, COM9, LPT0, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9 (ve AUX.txt dosyasından kaçının)
  • Dosya adı tüm dönemlerdir

Kontrol edilecek bazı isteğe bağlı şeyler:

  • Dosya yolları (dosya adı dahil) 260 karakterden uzun olamaz (\?\ önekini kullanmaz)
  • \?\ kullanılırken 32.000'den fazla karakter içeren Unicode dosya yolları (dosya adı dahil) (önekin dizin bileşenlerini genişletebileceğini ve bunun 32.000 sınırını aşmasına neden olabileceğini unutmayın)
116
user7116

.Net 3.5'ten önceki Altyapıları için bu çalışması gerekir:

Düzenli ifade eşleştirmesi size bir yol göstermelidir. İşte System.IO.Path.InvalidPathChars sabitini kullanan bir snippet;

bool IsValidFilename(string testName)
{
    Regex containsABadCharacter = new Regex("[" 
          + Regex.Escape(System.IO.Path.InvalidPathChars) + "]");
    if (containsABadCharacter.IsMatch(testName)) { return false; };

    // other checks for UNC, drive-path format, etc

    return true;
}

.Net Framework 3.0'dan sonraki için bu çalışmalıdır:

http://msdn.Microsoft.com/en-us/library/system.io.path.getinvalidpathchars(v=vs.90).aspx

Düzenli ifade eşleştirmesi size bir yol göstermelidir. İşte System.IO.Path.GetInvalidPathChars() sabitini kullanan bir snippet;

bool IsValidFilename(string testName)
{
    Regex containsABadCharacter = new Regex("["
          + Regex.Escape(new string(System.IO.Path.GetInvalidPathChars())) + "]");
    if (containsABadCharacter.IsMatch(testName)) { return false; };

    // other checks for UNC, drive-path format, etc

    return true;
}

Bunu öğrendikten sonra, farklı formatları da kontrol etmelisiniz, örneğin c:\my\drive ve \\server\share\dir\file.ext.

62
Steve Cooper

Kullanmayı deneyin ve hatayı yakalayın. İzin verilen set, dosya sistemlerinde veya Windows'un farklı sürümlerinde değişebilir. Başka bir deyişle, eğer Windows ismi beğenirse bilmek istiyorsanız, ismi verin ve size söylesin.

25

Bu sınıf dosya adlarını ve yollarını temizler; gibi kullan 

var myCleanPath = PathSanitizer.SanitizeFilename(myBadPath, ' ');

İşte kod;

/// <summary>
/// Cleans paths of invalid characters.
/// </summary>
public static class PathSanitizer
{
    /// <summary>
    /// The set of invalid filename characters, kept sorted for fast binary search
    /// </summary>
    private readonly static char[] invalidFilenameChars;
    /// <summary>
    /// The set of invalid path characters, kept sorted for fast binary search
    /// </summary>
    private readonly static char[] invalidPathChars;

    static PathSanitizer()
    {
        // set up the two arrays -- sorted once for speed.
        invalidFilenameChars = System.IO.Path.GetInvalidFileNameChars();
        invalidPathChars = System.IO.Path.GetInvalidPathChars();
        Array.Sort(invalidFilenameChars);
        Array.Sort(invalidPathChars);

    }

    /// <summary>
    /// Cleans a filename of invalid characters
    /// </summary>
    /// <param name="input">the string to clean</param>
    /// <param name="errorChar">the character which replaces bad characters</param>
    /// <returns></returns>
    public static string SanitizeFilename(string input, char errorChar)
    {
        return Sanitize(input, invalidFilenameChars, errorChar);
    }

    /// <summary>
    /// Cleans a path of invalid characters
    /// </summary>
    /// <param name="input">the string to clean</param>
    /// <param name="errorChar">the character which replaces bad characters</param>
    /// <returns></returns>
    public static string SanitizePath(string input, char errorChar)
    {
        return Sanitize(input, invalidPathChars, errorChar);
    }

    /// <summary>
    /// Cleans a string of invalid characters.
    /// </summary>
    /// <param name="input"></param>
    /// <param name="invalidChars"></param>
    /// <param name="errorChar"></param>
    /// <returns></returns>
    private static string Sanitize(string input, char[] invalidChars, char errorChar)
    {
        // null always sanitizes to null
        if (input == null) { return null; }
        StringBuilder result = new StringBuilder();
        foreach (var characterToTest in input)
        {
            // we binary search for the character in the invalid set. This should be lightning fast.
            if (Array.BinarySearch(invalidChars, characterToTest) >= 0)
            {
                // we found the character in the array of 
                result.Append(errorChar);
            }
            else
            {
                // the character was not found in invalid, so it is valid.
                result.Append(characterToTest);
            }
        }

        // we're done.
        return result.ToString();
    }

}
23
Steve Cooper

Kullandığım şey bu:

    public static bool IsValidFileName(this string expression, bool platformIndependent)
    {
        string sPattern = @"^(?!^(PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d|\..*)(\..+)?$)[^\x00-\x1f\\?*:\"";|/]+$";
        if (platformIndependent)
        {
           sPattern = @"^(([a-zA-Z]:|\\)\\)?(((\.)|(\.\.)|([^\\/:\*\?""\|<>\. ](([^\\/:\*\?""\|<>\. ])|([^\\/:\*\?""\|<>]*[^\\/:\*\?""\|<>\. ]))?))\\)*[^\\/:\*\?""\|<>\. ](([^\\/:\*\?""\|<>\. ])|([^\\/:\*\?""\|<>]*[^\\/:\*\?""\|<>\. ]))?$";
        }
        return (Regex.IsMatch(expression, sPattern, RegexOptions.CultureInvariant));
    }

İlk kalıp, yalnızca Windows platformları için geçersiz/yasadışı dosya adlarını ve karakterlerini içeren normal bir ifade oluşturur. İkincisi de aynısını yapar ancak adın herhangi bir platform için yasal olmasını sağlar.

22
Scott Dorman

Akılda tutulması gereken bir köşe davası, bunu ilk öğrendiğimde beni şaşırttı: Windows, dosya adlarında baştaki boşluk karakterlerine izin veriyor! Örneğin, aşağıdakilerin tümü Windows'taki yasal ve farklı dosya adlarıdır (eksi tırnak işaretleri):

"file.txt"
" file.txt"
"  file.txt"

Bundan paket servisi olanlardan biri: Bir dosya adı dizisindeki baştaki/sondaki boşlukları kesen kodu yazarken dikkatli olun.

18
Jon Schneider

Eugene Katz'in cevabını basitleştirmek:

bool IsFileNameCorrect(string fileName){
    return !fileName.Any(f=>Path.GetInvalidFileNameChars().Contains(f))
}

Veya

bool IsFileNameCorrect(string fileName){
    return fileName.All(f=>!Path.GetInvalidFileNameChars().Contains(f))
}
8
tmt

Microsoft Windows: Windows çekirdeği, 1-31 aralığında (yani, 0x01-0x1F) ve "*: <>?\| Karakterlerinin kullanılmasını yasaklar. NTFS, her yol bileşeninin (dizin veya dosya adı) 255 karakter uzunluğunda olmasına ve yaklaşık 32767 karakter uzunluğundaki yollar, Windows çekirdeği yalnızca 259 karakter uzunluğundaki yolları destekler.Ayrıca, Windows, MS-DOS aygıt adlarının AUX, CLOCK $, COM1, COM2, COM3, COM4, ​​COM5, COM6, COM7, COM8, COM9, CON, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9, NUL ve PRN ve bunların yanı sıra herhangi bir uzantıya sahip (örneğin, AUX.txt) Uzun UNC yolları (ör. \.\C:\nul.txt veya \?\D:\aux\con). (Aslında, bir uzantı sağlanmışsa CLOCK $ kullanılabilir.) Bu kısıtlamalar yalnızca Windows için geçerlidir - Örneğin Linux, "*: <> kullanımına izin verir?\| NTFS'de bile.

Kaynak: http://en.wikipedia.org/wiki/Filename

8
Martin Faartoft

Tüm olası karakterleri açıkça belirtmektense, geçersiz karakterlerin olup olmadığını kontrol etmek ve ardından bir hatayı bildirmek için regex yapabilirsiniz. İdeal olarak uygulamanız dosyaları tam olarak kullanıcının istediği şekilde adlandırmalı ve yalnızca bir hataya rastlarsa ağlatmalıdır.

7
ConroyP

Bunu istisnalar atmadan dosya adlarındaki geçersiz karakterlerden kurtulmak için kullanıyorum:

private static readonly Regex InvalidFileRegex = new Regex(
    string.Format("[{0}]", Regex.Escape(@"<>:""/\|?*")));

public static string SanitizeFileName(string fileName)
{
    return InvalidFileRegex.Replace(fileName, string.Empty);
}
6
JoelFan

Ayrıca CON, PRN, AUX, NUL, COM # ve diğer birkaçı hiçbir uzantıya sahip herhangi bir dizinde yasal dosya adı değildir.

5
Roland Rabien

Soru, bir yol adının yasal bir Windows yolu olup olmadığını mı belirlemeye çalışıyorsunuz veya yasal mı kodun çalıştığı sistemde.? İkincisi daha önemli olduğunu düşünüyorum, bu yüzden kişisel olarak, muhtemelen tam yolu çözer ve dosyanın ait olduğu dizini oluşturmak için _mkdir komutunu kullanmayı denerim, sonra dosyayı oluşturmayı deneyin.

Bu yolla, yalnızca yolun yalnızca geçerli Windows karakterleri içerdiğini değil, aynı zamanda bu işlem tarafından yazılabilecek bir yolu temsil ettiğini de bilirsiniz.

5
kfh

Diğer cevapları tamamlamak için, işte size düşünmek isteyebileceğiniz birkaç ek Edge durumu var.

4
Joe

MSDN 'den, izin verilmeyen karakterlerin bir listesi:

Unicode karakterleri ve genişletilmiş karakter kümesindeki karakterleri (128-255) içeren bir ad için geçerli kod sayfasındaki hemen hemen her karakteri kullanın:

  • Aşağıdaki ayrılmış karakterlere izin verilmez: <>: "/\|? *
  • Tamsayılı gösterimi sıfır ile 31 arasında olan karakterlere izin verilmez.
  • Hedef dosya sisteminin izin vermediği herhangi bir karakter.
3
Mark Biek

Bu durum için düzenli ifadeler gereğinden fazladır. String.IndexOfAny() yöntemini Path.GetInvalidPathChars() ve Path.GetInvalidFileNameChars() ile birlikte kullanabilirsiniz.

Ayrıca, her iki Path.GetInvalidXXX() yönteminin de bir iç diziyi klonladığını ve klonu döndürdüğünü unutmayın. Dolayısıyla, bunu çok fazla (binlerce ve binlerce kez) yapacaksanız, geçersiz karakter dizisinin bir kopyasını yeniden kullanmak üzere önbelleğe alabilirsiniz.

2
s n

Ayrıca hedef dosya sistemi önemlidir.

NTFS'de, bazı dosyalar belirli dizinlerde oluşturulamaz . E.G. $ Kökte boot 

2
Dominik Weber

Bu çoktan cevaplanmış bir soru, ancak sadece "Diğer seçenekler" için, işte ideal olmayan bir soru:

(İdeal değil, çünkü Özel Durumları akış kontrolü olarak kullanmak genellikle "Kötü Şey" dir)

public static bool IsLegalFilename(string name)
{
    try 
    {
        var fileInfo = new FileInfo(name);
        return true;
    }
    catch
    {
        return false;
    }
}
2
JerKimball

Yalnızca dosya adınızı/yolunuzu tutan bir dizgede geçersiz karakterler olup olmadığını kontrol etmeye çalışıyorsanız, bulduğum en hızlı yöntem, dosya adını geçersiz bir karakterin olduğu her yerde bir parça dizisine bölmek için Split() işlevini kullanmaktır. Sonuç sadece 1 dizisi ise, geçersiz karakter yoktur. :-)

var nameToTest = "Best file name \"ever\".txt";
bool isInvalidName = nameToTest.Split(System.IO.Path.GetInvalidFileNameChars()).Length > 1;

var pathToTest = "C:\\My Folder <secrets>\\";
bool isInvalidPath = pathToTest.Split(System.IO.Path.GetInvalidPathChars()).Length > 1;

Bunu ve yukarıda belirtilen diğer yöntemleri LinqPad'de 1.000.000 kez bir dosya/yol adı üzerinde çalıştırmayı denedim.

Split() işlevini kullanmak sadece ~ 850ms'dir.

Regex("[" + Regex.Escape(new string(System.IO.Path.GetInvalidPathChars())) + "]") işlevini kullanmak yaklaşık 6 saniyedir.

Daha karmaşık olan normal ifadeler, diğer seçeneklerden bazılarında olduğu gibi, daha değişkendir. __Dosyayı almak için Path sınıfındaki çeşitli yöntemleri kullanmak ve dahili onaylamalarının işi yapmasına izin vermek (büyük olasılıkla istisna işlemesi nedeniyle).

1 milyon dosya adını doğrulamanız gerekmediği için, bu yöntemlerin çoğu için tek bir yineleme yeterlidir. Ancak, yalnızca geçersiz karakterler arıyorsanız, hala oldukça verimli ve etkilidir.

1
Nick Albrecht

dosya adı çok uzunsa ve Windows 10 öncesi bir ortamda çalışıyorsa, bu yanıtların çoğu çalışmayacaktır. Benzer şekilde, periyotlarla ne yapmak istediğinizi de düşünün - öncül veya ize izin vermek teknik olarak geçerlidir, ancak dosyanın sırasıyla görmesini veya silinmesini zor istemiyorsanız sorunlara neden olabilir.

Bu, geçerli bir dosya adını kontrol etmek için oluşturduğum bir doğrulama özelliğidir. 

public class ValidFileNameAttribute : ValidationAttribute
{
    public ValidFileNameAttribute()
    {
        RequireExtension = true;
        ErrorMessage = "{0} is an Invalid Filename";
        MaxLength = 255; //superseeded in modern windows environments
    }
    public override bool IsValid(object value)
    {
        //http://stackoverflow.com/questions/422090/in-c-sharp-check-that-filename-is-possibly-valid-not-that-it-exists
        var fileName = (string)value;
        if (string.IsNullOrEmpty(fileName)) { return true;  }
        if (fileName.IndexOfAny(Path.GetInvalidFileNameChars()) > -1 ||
            (!AllowHidden && fileName[0] == '.') ||
            fileName[fileName.Length - 1]== '.' ||
            fileName.Length > MaxLength)
        {
            return false;
        }
        string extension = Path.GetExtension(fileName);
        return (!RequireExtension || extension != string.Empty)
            && (ExtensionList==null || ExtensionList.Contains(extension));
    }
    private const string _sepChar = ",";
    private IEnumerable<string> ExtensionList { get; set; }
    public bool AllowHidden { get; set; }
    public bool RequireExtension { get; set; }
    public int MaxLength { get; set; }
    public string AllowedExtensions {
        get { return string.Join(_sepChar, ExtensionList); } 
        set {
            if (string.IsNullOrEmpty(value))
            { ExtensionList = null; }
            else {
                ExtensionList = value.Split(new char[] { _sepChar[0] })
                    .Select(s => s[0] == '.' ? s : ('.' + s))
                    .ToList();
            }
    } }

    public override bool RequiresValidationContext => false;
}

ve testler

[TestMethod]
public void TestFilenameAttribute()
{
    var rxa = new ValidFileNameAttribute();
    Assert.IsFalse(rxa.IsValid("pptx."));
    Assert.IsFalse(rxa.IsValid("pp.tx."));
    Assert.IsFalse(rxa.IsValid("."));
    Assert.IsFalse(rxa.IsValid(".pp.tx"));
    Assert.IsFalse(rxa.IsValid(".pptx"));
    Assert.IsFalse(rxa.IsValid("pptx"));
    Assert.IsFalse(rxa.IsValid("a/abc.pptx"));
    Assert.IsFalse(rxa.IsValid("a\\abc.pptx"));
    Assert.IsFalse(rxa.IsValid("c:abc.pptx"));
    Assert.IsFalse(rxa.IsValid("c<abc.pptx"));
    Assert.IsTrue(rxa.IsValid("abc.pptx"));
    rxa = new ValidFileNameAttribute { AllowedExtensions = ".pptx" };
    Assert.IsFalse(rxa.IsValid("abc.docx"));
    Assert.IsTrue(rxa.IsValid("abc.pptx"));
}
1
Brent

Benim girişimim:

using System.IO;

static class PathUtils
{
  public static string IsValidFullPath([NotNull] string fullPath)
  {
    if (string.IsNullOrWhiteSpace(fullPath))
      return "Path is null, empty or white space.";

    bool pathContainsInvalidChars = fullPath.IndexOfAny(Path.GetInvalidPathChars()) != -1;
    if (pathContainsInvalidChars)
      return "Path contains invalid characters.";

    string fileName = Path.GetFileName(fullPath);
    if (fileName == "")
      return "Path must contain a file name.";

    bool fileNameContainsInvalidChars = fileName.IndexOfAny(Path.GetInvalidFileNameChars()) != -1;
    if (fileNameContainsInvalidChars)
      return "File name contains invalid characters.";

    if (!Path.IsPathRooted(fullPath))
      return "The path must be absolute.";

    return "";
  }
}

Bu mükemmel değildir, çünkü Path.GetInvalidPathChars, dosya ve dizin adlarında geçersiz olan karakter kümesinin tamamını döndürmez ve elbette çok daha fazla incelik vardır.

Bu yüzden bu yöntemi tamamlayıcı olarak kullanıyorum:

public static bool TestIfFileCanBeCreated([NotNull] string fullPath)
{
  if (string.IsNullOrWhiteSpace(fullPath))
    throw new ArgumentException("Value cannot be null or whitespace.", "fullPath");

  string directoryName = Path.GetDirectoryName(fullPath);
  if (directoryName != null) Directory.CreateDirectory(directoryName);
  try
  {
    using (new FileStream(fullPath, FileMode.CreateNew)) { }
    File.Delete(fullPath);
    return true;
  }
  catch (IOException)
  {
    return false;
  }
}

Bir istisna varsa dosyayı oluşturmaya ve yanlış döndürmeye çalışır. Elbette, dosyayı oluşturmam gerekiyor ama bunun en güvenli yol olduğunu düşünüyorum. Lütfen ayrıca oluşturulan dizinleri silmediğime dikkat edin.

Temel doğrulama yapmak için ilk yöntemi de kullanabilir ve ardından yol kullanıldığında istisnaları dikkatle ele alabilirsiniz.

1
Maxence

Benim düşünceme göre, bu sorunun tek doğru yolu yolu kullanmaya çalışmak ve işletim sistemi ile dosya sisteminin doğrulamasını sağlamak. Aksi halde, işletim sistemi ve dosya sisteminin zaten kullandığı tüm doğrulama kurallarını yeniden uygularsınız (ve muhtemelen kötü şekilde) ve gelecekte bu kurallar değiştirilirse kodunuzu bunlarla eşleşecek şekilde değiştirmeniz gerekecektir.

0
Igor Levicki

Bu çek

static bool IsValidFileName(string name)
{
    return
        !string.IsNullOrWhiteSpace(name) &&
        name.IndexOfAny(Path.GetInvalidFileNameChars()) < 0 &&
        !Path.GetFullPath(name).StartsWith(@"\\.\");
}

geçersiz karakterler (<>:"/\|?* ve ASCII 0-31) ve ayrılmış DOS aygıtları (CON, NUL, COMx) olan adları filtreler. Path.GetFullPath ile tutarlı olarak önde gelen boşluklara ve tüm nokta adlarına izin verir. (Baştaki boşluklarla dosya oluşturma sistemimde başarılı oluyor).


Kullanılan .NET Framework 4.7.1, Windows 7'de test edilmiştir.

0
Vlad

Bu fikri birinden aldım. - kim olduğunu bilmiyorum. İşletim sisteminin ağır kaldırma yapmasına izin verin.

public bool IsPathFileNameGood(string fname)
{
    bool rc = Constants.Fail;
    try
    {
        this._stream = new StreamWriter(fname, true);
        rc = Constants.Pass;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Problem opening file");
        rc = Constants.Fail;
    }
    return rc;
}
0
KenR

Dizedeki illigal karakterlerini doğrulamak için kullanılan bir liner:

public static bool IsValidFilename(string testName) => !Regex.IsMatch(testName, "[" + Regex.Escape(new string(System.IO.Path.InvalidPathChars)) + "]");
0
Zananok

Sadece Path.GetFullPath () kullanmanızı öneririm

string tagetFileFullNameToBeChecked;
try
{
  Path.GetFullPath(tagetFileFullNameToBeChecked)
}
catch(AugumentException ex)
{
  // invalid chars found
}
0
Tony Sun