it-swarm-id.com

Bagaimana cara saya memeriksa apakah string yang diberikan adalah nama file yang sah/sah pada Windows?

Saya ingin memasukkan fungsi mengubah nama file batch dalam aplikasi saya. Seorang pengguna dapat mengetikkan pola nama tujuan dan (setelah mengganti beberapa wildcard dalam pola) Saya perlu memeriksa apakah itu akan menjadi nama file yang sah di Windows. Saya sudah mencoba menggunakan ekspresi reguler seperti [a-zA-Z0-9_]+ tetapi tidak menyertakan banyak karakter khusus nasional dari berbagai bahasa (mis. Umlauts dan sebagainya). Apa cara terbaik untuk melakukan pemeriksaan seperti itu?

150
tomash

Anda bisa mendapatkan daftar karakter yang tidak valid dari Path.GetInvalidPathChars dan GetInvalidFileNameChars .

UPD: Lihat Saran Steve Cooper tentang cara menggunakan ini dalam ekspresi reguler.

UPD2: Perhatikan bahwa menurut bagian Keterangan di MSDN "Array yang dikembalikan dari metode ini tidak dijamin mengandung set karakter lengkap yang tidak valid dalam nama file dan direktori." Jawaban yang diberikan oleh sixlettervaliables masuk ke rincian lebih lanjut.

96
Eugene Katz

Dari "Penamaan File atau Direktori" MSDN, " berikut adalah konvensi umum untuk nama file hukum apa di bawah Windows:

Anda dapat menggunakan karakter apa pun di halaman kode saat ini (Unicode/ANSI di atas 127), kecuali:

  • <>:"/\|?*
  • Karakter yang representasi bilangan bulatnya adalah 0-31 (kurang dari ASCII spasi)
  • Karakter lain apa pun yang tidak diizinkan oleh sistem file target (katakanlah, periode atau spasi tambahan)
  • Nama DOS: CON, PRN, AUX, NUL, COM0, COM1, COM2, COM3, COM5, COM6, COM7, COM8, COM9, LPT0, LPT1, LPT2, LPT3, LPT4, LPT5, LPT7, LPT7, LPT8, LPT9 (dan hindari AUX.txt, dll)
  • Nama file adalah semua periode

Beberapa hal opsional untuk diperiksa:

  • Jalur file (termasuk nama file) mungkin tidak memiliki lebih dari 260 karakter (yang tidak menggunakan awalan \?\)
  • Jalur file Unicode (termasuk nama file) dengan lebih dari 32.000 karakter saat menggunakan \?\ (perhatikan bahwa awalan dapat memperluas komponen direktori dan menyebabkannya melebihi batas 32.000)
116
user7116

Untuk Kerangka .Net sebelum 3,5 ini harus berfungsi:

Pencocokan ekspresi reguler akan membantu Anda. Berikut cuplikan menggunakan konstanta System.IO.Path.InvalidPathChars;

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

Untuk .Net Frameworks setelah 3.0 ini seharusnya berfungsi:

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

Pencocokan ekspresi reguler akan membantu Anda. Berikut cuplikan menggunakan konstanta System.IO.Path.GetInvalidPathChars();

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

Setelah Anda tahu itu, Anda juga harus memeriksa format yang berbeda, misalnya c:\my\drive dan \\server\share\dir\file.ext

62
Steve Cooper

Cobalah untuk menggunakannya, dan jebak kesalahannya. Perangkat yang diizinkan dapat berubah di seluruh sistem file, atau di berbagai versi Windows. Dengan kata lain, jika Anda ingin tahu apakah Windows menyukai namanya, berikan nama itu dan beri tahu.

25

Kelas ini membersihkan nama file dan jalur; gunakan seperti 

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

Ini kodenya;

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

Inilah yang saya gunakan:

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

Pola pertama membuat ekspresi reguler yang berisi nama file dan karakter yang tidak valid/ilegal hanya untuk platform Windows. Yang kedua melakukan hal yang sama tetapi memastikan bahwa nama tersebut legal untuk platform apa pun.

22
Scott Dorman

Satu hal yang perlu diingat, yang mengejutkan saya ketika saya pertama kali mengetahuinya: Windows memungkinkan karakter ruang terkemuka dalam nama file! Misalnya, berikut ini semua nama file yang sah dan berbeda pada Windows (minus tanda kutip):

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

Satu hal yang bisa diambil dari ini: Berhati-hatilah saat menulis kode yang memotong spasi spasi awal/akhir dari string nama file.

18
Jon Schneider

Menyederhanakan jawaban Eugene Katz:

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

Atau

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

Microsoft Windows: Kernel Windows melarang penggunaan karakter dalam rentang 1-31 (yaitu, 0x01-0x1F) dan karakter "*: <>?\|. Meskipun NTFS memungkinkan setiap komponen jalur (direktori atau nama file) panjangnya 255 karakter dan lintasan hingga 32767 karakter, kernel Windows hanya mendukung lintasan hingga 259 karakter. Selain itu, Windows melarang penggunaan nama perangkat MS-DOS AUX, CLOCK $, COM1, COM2, COM3, COM4, ​​COM5, COM6, COM7, COM8, COM9, CON, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9, NUL dan PRN, serta nama-nama ini dengan ekstensi apa pun (misalnya, AUX.txt), kecuali saat menggunakan Jalur UNC yang panjang (mis. \.\C:\nul.txt atau \?\D:\aux\con). (Faktanya, CLOCK $ dapat digunakan jika ekstensi diberikan.) Pembatasan ini hanya berlaku untuk Windows - Linux, misalnya, memungkinkan penggunaan "*: <>?\| bahkan di NTFS.

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

8
Martin Faartoft

Daripada secara eksplisit memasukkan semua karakter yang mungkin, Anda bisa melakukan regex untuk memeriksa keberadaan karakter ilegal, dan melaporkan kesalahan saat itu. Idealnya aplikasi Anda harus memberi nama file persis seperti yang diinginkan pengguna, dan hanya menangis busuk jika menemukan kesalahan.

7
ConroyP

Saya menggunakan ini untuk menyingkirkan karakter yang tidak valid dalam nama file tanpa membuang pengecualian:

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

Juga CON, PRN, AUX, NUL, COM # dan beberapa lainnya tidak pernah nama file hukum dalam direktori apa pun dengan ekstensi apa pun.

5
Roland Rabien

Pertanyaannya adalah apakah Anda mencoba menentukan apakah nama jalur adalah jalur jendela yang sah, atau apakah itu legal pada sistem tempat kode berjalan. ? Saya pikir yang terakhir lebih penting, jadi secara pribadi, saya mungkin akan menguraikan path lengkap dan mencoba menggunakan _mkdir untuk membuat direktori milik file, kemudian mencoba membuat file.

Dengan cara ini Anda tahu tidak hanya jika lintasan hanya berisi karakter windows yang valid, tetapi jika lintasan tersebut benar-benar mewakili lintasan yang dapat ditulis oleh proses ini.

5
kfh

Untuk melengkapi jawaban lain, berikut adalah beberapa kasus Edge tambahan yang mungkin ingin Anda pertimbangkan.

4
Joe

Dari MSDN , berikut adalah daftar karakter yang tidak diizinkan:

Gunakan hampir semua karakter di halaman kode saat ini untuk sebuah nama, termasuk karakter Unicode dan karakter dalam set karakter diperluas (128–255), kecuali untuk yang berikut:

  • Karakter yang dipesan berikut ini tidak diizinkan: <>: "/\|? *
  • Karakter yang representasi bilangan bulatnya berkisar dari nol hingga 31 tidak dibolehkan.
  • Karakter lain apa pun yang tidak diizinkan oleh sistem file target.
3
Mark Biek

Ekspresi reguler berlebihan untuk situasi ini. Anda dapat menggunakan metode String.IndexOfAny() dalam kombinasi dengan Path.GetInvalidPathChars() dan Path.GetInvalidFileNameChars().

Perhatikan juga bahwa kedua metode Path.GetInvalidXXX() mengkloning array internal dan mengembalikan clone. Jadi, jika Anda akan sering melakukan ini (ribuan dan ribuan kali) Anda bisa menyimpan salinan array array yang tidak valid untuk digunakan kembali.

2
s n

Sistem file tujuan juga penting.

Di bawah NTFS, beberapa file tidak dapat dibuat di direktori tertentu . E.G. $ Boot di root 

2
Dominik Weber

Ini adalah pertanyaan yang sudah dijawab, tetapi hanya demi "Pilihan lain", berikut ini adalah pertanyaan yang tidak ideal:

(tidak ideal karena menggunakan Pengecualian sebagai kontrol aliran adalah "Hal Buruk", umumnya)

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

Jika Anda hanya mencoba memeriksa apakah string yang menyimpan nama/jalur file Anda memiliki karakter yang tidak valid, metode tercepat yang saya temukan adalah menggunakan Split() untuk memecah nama file menjadi array bagian di mana pun ada karakter yang tidak valid. Jika hasilnya hanya array 1, tidak ada karakter yang tidak valid. :-)

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;

Saya mencoba menjalankan ini dan metode lain yang disebutkan di atas pada nama file/path 1.000.000 kali di LinqPad.

Menggunakan Split() hanya ~ 850ms.

Menggunakan Regex("[" + Regex.Escape(new string(System.IO.Path.GetInvalidPathChars())) + "]") adalah sekitar 6 detik.

Ekspresi reguler yang lebih rumit jauh lebih buruk, seperti halnya beberapa opsi lain, seperti menggunakan berbagai metode pada kelas Path untuk mendapatkan nama file dan membiarkan validasi internal mereka melakukan pekerjaan (kemungkinan besar karena overhead penanganan pengecualian).

Memang tidak terlalu sering Anda perlu memvalidasi 1 juta nama file, jadi iterasi tunggal tidak masalah untuk sebagian besar metode ini. Tapi itu masih cukup efisien dan efektif jika Anda hanya mencari karakter yang tidak valid.

1
Nick Albrecht

banyak dari jawaban ini tidak akan berfungsi jika nama file terlalu panjang & berjalan di lingkungan pra Windows 10. Demikian pula, pikirkan tentang apa yang ingin Anda lakukan dengan titik - memungkinkan memimpin atau mengikuti secara teknis valid, tetapi dapat membuat masalah jika Anda tidak ingin file menjadi sulit dilihat atau dihapus masing-masing.

Ini adalah atribut validasi yang saya buat untuk memeriksa nama file yang valid. 

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

dan tes

[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

Usaha saya:

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

Ini tidak sempurna karena Path.GetInvalidPathChars tidak mengembalikan set lengkap karakter yang tidak valid dalam nama file dan direktori dan tentu saja ada banyak lagi kehalusan.

Jadi saya menggunakan metode ini sebagai pelengkap:

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

Mencoba membuat file dan mengembalikan false jika ada pengecualian. Tentu saja, saya perlu membuat file tetapi saya pikir itu cara paling aman untuk melakukannya. Harap perhatikan juga bahwa saya tidak menghapus direktori yang telah dibuat.

Anda juga bisa menggunakan metode pertama untuk melakukan validasi dasar, dan kemudian menangani dengan hati-hati pengecualian saat jalur digunakan.

1
Maxence

Cek ini

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

memfilter nama dengan karakter yang tidak valid (<>:"/\|?* dan ASCII 0-31), serta perangkat DOS yang dicadangkan (CON, NUL, COMx). Ini memungkinkan spasi dan semua nama titik terkemuka, konsisten dengan Path.GetFullPath. (Membuat file dengan spasi terdepan berhasil di sistem saya).


Digunakan .NET Framework 4.7.1, diuji pada Windows 7.

0
Vlad

Saya mendapat ide ini dari seseorang. - tidak tahu siapa. Biarkan OS melakukan angkat berat.

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

Saya sarankan gunakan saja Path.GetFullPath ()

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