如何在Windows下检查给定字符串是否是合法/有效的文件名?

tom*_*ash 156 c# windows filesystems file

我想在我的应用程序中包含批处理文件重命名功能.用户可以键入目标文件名模式和(在替换模式中的一些通配符之后)我需要检查它是否是Windows下的合法文件名.我试过使用正则表达式,[a-zA-Z0-9_]+但它不包括来自各种语言的许多国家特定字符(例如变音符号等).做这种检查的最佳方法是什么?

use*_*116 120

MSDN的"命名文件或目录",以下是Windows下合法文件名的一般约定:

您可以使用当前代码页中的任何字符(Unicode/ANSI高于127),除了:

  • < > : " / \ | ? *
  • 整数表示为0-31(小于ASCII空格)的字符
  • 目标文件系统不允许的任何其他字符(例如,尾随句点或空格)
  • 任何DOS名称:CON,PRN,AUX,NUL,COM0,COM1,COM2,COM3,COM4,COM5,COM6,COM7,COM8,COM9,LPT0,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7, LPT8,LPT9(并避免AUX.txt等)
  • 文件名是所有句点

一些可选的东西要检查:

  • 文件路径(包括文件名)不得超过260个字符(不使用\?\前缀)
  • 使用时具有超过32,000个字符的Unicode文件路径(包括文件名)\?\(请注意,前缀可能会扩展目录组件并导致其溢出32,000个限制)

  • 上面提到的所有这些条件的正确正则表达式如下:`Regex unspupportedRegex = new Regex("(^(PRN | AUX | NUL | CON | COM [1-9] | LPT [1-9] |(\\. +)$)(\\ ..*)$)|(([\\ X00 - \\ X1F \\\\*?:\"; |/<>]?)+)|((\\. ] +)",RegexOptions.IgnoreCase);` (9认同)
  • 包含保留文件名的+1 - 在之前的答案中遗漏了这些文件名. (8认同)
  • "CLOCK $"怎么样? (4认同)
  • @whywhywhy我认为你在Regex中有一个额外的开场支架."(^(PRN | AUX | NUL | CON | COM [1-9] | LPT [1-9] |(\\ +)$)(\\.*)$?)|(([\\ x00 - \\ x1f \\\\?*:\"; |/<>])+)|([\\.] +)"为我工作. (4认同)
  • 如果使用"\\?\"语法,"AUX"是一个完全可用的文件名.当然,不使用该语法的程序在处理它时存在实际问题...(在XP上测试) (2认同)
  • 取决于您如何定义“允许”。_Windows_ 允许以点开头的文件名,但 _Explorer_ 不允许您这样命名文件,除非也有扩展名。例如,不允许使用 `.foo`,但允许使用 `.foo.bar`。 (2认同)
  • 我读了这篇答案中提到的同一篇文章,并通过实验发现COM0和LPT0也是不允许的.@dlf这个使用以'.'开头的文件名:`^(?!^(?:PRN | AUX | CLOCK\$ | NUL | CON | COM\d | LPT\d)(?:\ .. + ?)$)(:\*(\)).?!?!:; | [\ s]的[^\x00-\X1F \\*?\"\/<>] +(<) $` (2认同)

Eug*_*atz 97

您可以从Path.GetInvalidPathChars和获取无效字符列表GetInvalidFileNameChars.

UPD:请参阅Steve Cooper关于如何在正则表达式中使用这些内容的建议.

UPD2:请注意,根据MSDN中的"备注"部分,"不保证从此方法返回的数组包含在文件和目录名称中无效的完整字符集".sixlettervaliables提供的答案更详细.

  • 是否有人对MS没有为此功能提供系统级功能/ API感到失望,而不是每个开发人员必须自己烹饪他/她自己的解决方案?想知道是否有一个非常好的理由,或只是对MS部分的疏忽. (29认同)
  • 这不回答问题; 有许多字符串只包含有效字符(例如"....","CON",字符串数百个字符长),这些字符串不是有效的文件名. (11认同)

Ste*_*per 63

对于3.5之前的.Net框架,这应该工作:

正则表达式匹配应该可以帮到你.这是使用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;
}
Run Code Online (Sandbox Code Playgroud)

对于3.0之后的.Net框架,这应该工作:

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

正则表达式匹配应该可以帮到你.这是使用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;
}
Run Code Online (Sandbox Code Playgroud)

一旦你知道了,你还应该检查不同的格式,例如c:\my\drive\\server\share\dir\file.ext

  • string strThisAreInvalidFileNameChars = new string( System.IO.Path.GetInvalidFileNameChars() ) ; Regex regFixFileName = new Regex("[" + Regex.Escape(strThisAreInvalidFileNameChars) + "]"); (29认同)
  • 人们的一点研究可以创造奇迹.我已更新帖子以反映更改. (2认同)

小智 25

尝试使用它,并捕获错误.允许的集可能会跨文件系统或跨不同版本的Windows进行更改.换句话说,如果您想知道Windows是否喜欢该名称,请将其命名并让它告诉您.

  • @gap因为它并不总是有效.例如,尝试访问CON通常会成功,即使它不是真正的文件. (4认同)
  • 尽管如此,尽可能避免抛出Exception的内存开销总是更好. (3认同)
  • 这似乎是唯一一个针对所有约束进行测试的方法。为什么选择其他答案而不是这个? (2认同)
  • 此外,您可能无权访问它;例如,通过编写来测试它,即使您可以阅读它,如果它确实存在或将存在。 (2认同)

Ste*_*per 23

该类清除文件名和路径; 用它就像

var myCleanPath = PathSanitizer.SanitizeFilename(myBadPath, ' ');
Run Code Online (Sandbox Code Playgroud)

这是代码;

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

}
Run Code Online (Sandbox Code Playgroud)


Sco*_*man 22

这是我使用的:

    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));
    }
Run Code Online (Sandbox Code Playgroud)

第一个模式创建一个正则表达式,其中仅包含Windows平台的无效/非法文件名和字符.第二个做同样的事情,但确保该名称对任何平台都是合法的.

  • sPattern正则表达式不允许以句点字符开头的文件.但[MSDN说](http://msdn.microsoft.com/en-us/library/aa365247.aspx)"可以指定句点作为名称的第一个字符.例如,".temp"" .我会删除"\ ..*"以使.gitignore正确的文件名:) (4认同)

Jon*_*der 18

记住一个角落的情况,当我第一次发现它时让我感到惊讶:Windows允许文件名中的前导空格字符!例如,以下是Windows上的所有合法且不同的文件名(减去引号):

"file.txt"
" file.txt"
"  file.txt"
Run Code Online (Sandbox Code Playgroud)

从中可以看出:在编写用于修剪文件名字符串中的前导/尾随空格的代码时要小心.


tmt*_*tmt 9

简化Eugene Katz的答案:

bool IsFileNameCorrect(string fileName){
    return !fileName.Any(f=>Path.GetInvalidFileNameChars().Contains(f))
}
Run Code Online (Sandbox Code Playgroud)

要么

bool IsFileNameCorrect(string fileName){
    return fileName.All(f=>!Path.GetInvalidFileNameChars().Contains(f))
}
Run Code Online (Sandbox Code Playgroud)


Mar*_*oft 8

Microsoft Windows:Windows内核禁止使用范围1-31(即0x01-0x1F)和字符"*:<>?\ |中的字符.虽然NTFS允许每个路径组件(目录或文件名)长度为255个字符,最长约32767个字符的路径,Windows内核仅支持长达259个字符的路径.此外,Windows禁止使用MS-DOS设备名称AUX,CLOCK $,COM1,COM2,COM3,COM4,COM5,COM6, COM7,COM8,COM9,CON,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7,LPT8,LPT9,NUL和PRN,以及带有任何扩展名的这些名称(例如,AUX.txt),使用时除外长UNC路径(例如\.\ C:\nul.txt或\?\ D:\ aux\con).(实际上,如果提供了扩展,则可以使用CLOCK $.)这些限制仅适用于Windows - 例如,Linux允许使用"*:<>?\ | 甚至在NTFS.

资料来源:http://en.wikipedia.org/wiki/Filename


Con*_*oyP 7

您可以使用正则表达式检查是否存在非法字符,然后报告错误,而不是明确包含所有可能的字符.理想情况下,您的应用程序应该按照用户的意愿命名文件,并且只有在遇到错误时才会犯规.


kfh*_*kfh 6

问题是您是在尝试确定路径名是否是合法的Windows路径,或者它是否在运行代码的系统上是合法的.?我认为后者更重要,所以个人而言,我可能会分解完整路径并尝试使用_mkdir来创建文件所属的目录,然后尝试创建该文件.

这样,您不仅知道路径是否仅包含有效的窗口字符,而且它实际上是否表示此进程可以写入的路径.


Joe*_*Fan 6

我使用它来摆脱文件名中的无效字符而不抛出异常:

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

public static string SanitizeFileName(string fileName)
{
    return InvalidFileRegex.Replace(fileName, string.Empty);
}
Run Code Online (Sandbox Code Playgroud)


小智 5

此外,CON,PRN,AUX,NUL,COM#和其他一些内容在任何具有任何扩展名的目录中都不是合法的文件名.