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

时间:2020-03-05 18:53:26  来源:igfitidea点击:

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

解决方案

回答

我们可以执行正则表达式来检查是否存在非法字符,而不是显式包括所有可能的字符,然后报告错误。理想情况下,应用程序应完全按照用户希望的名称命名文件,并且仅当偶然发现错误时才鸣叫。

回答

在MSDN中,这是不允许的字符列表:

Use almost any character in the current code page for a name, including Unicode characters and characters in the extended character set (128–255), except for the following:
        
        
        The following reserved characters are not allowed:
         < > : " / \ | ? *
        Characters whose integer representations are in the range from zero through 31 are not allowed.
        Any other character that the target file system does not allow.

回答

我们可以从Path.GetInvalidPathChars和GetInvalidFileNameChars中获取无效字符列表。

UPD:有关如何在正则表达式中使用它们的信息,请参阅Steve Cooper的建议。

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

回答

Windows文件名没有限制,因此实际上它甚至不是什么大问题。 Windows禁止使用的字符是:

\ / : * ? " < > |

我们可以轻松地编写一个表达式来检查这些字符是否存在。不过,更好的解决方案是尝试根据用户需要命名文件,并在文件名不粘时发出警报。

回答

此外,CON,PRN,AUX,NUL,COM和其他一些名称在任何扩展名的目录中都永远不是合法的文件名。

回答

Microsoft Windows:Windows内核禁止使用范围为1-31的字符(即0x01-0x1F)和字符" *:<>?\ |。尽管NTFS允许每个路径组件(目录或者文件名)的长度为255个字符,并且Windows内核最多可以包含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/文件名

回答

对于3.5之前的.Net Frameworks,它应该可以工作:

正则表达式匹配应该为我们提供一些帮助。这是使用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;
}

对于3.0之后的.Net Frameworks,它应该可以工作:

http://msdn.microsoft.com/zh-cn/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;
}

知道之后,我们还应该检查其他格式,例如c:\ my \ drive和`\ server \ share \ dir \ file.ext

回答

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

这样,我们不仅知道路径是否仅包含有效的Windows字符,而且还知道它是否实际代表可以由该过程写入的路径。

回答

在MSDN的"命名文件或者目录"中,以下是Windows下合法文件名的常规约定:

我们可以在当前代码页中使用任何字符(Unicode / ANSI高于127),但以下情况除外:

  • &lt;``>``:``````/``\``|```?``*
  • 整数表示形式为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个字符(不使用\?\前缀)
  • 使用\\时,Unicode文件路径(包括文件名)的字符数超过32,000(请注意,前缀可能会扩展目录组件,并导致其超出32,000的限制)

回答

尝试使用它,并捕获错误。允许的集合可能会在文件系统之间或者Windows的不同版本中发生变化。换句话说,如果我们想知道Windows是否喜欢该名称,请将该名称递给我们,然后告诉我们。

回答

这是我用的:

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

第一个模式创建一个正则表达式,仅包含Windows平台的无效/非法文件名和字符。第二个功能相同,但确保名称适用于任何平台。

回答

对于这种情况,正则表达式会显得过分杀伤力。我们可以将String.IndexOfAny()方法与Path.GetInvalidPathChars()和Path.GetInvalidFileNameChars()结合使用。

还要注意,两个Path.GetInvalidXXX()方法都克隆一个内部数组并返回克隆。因此,如果我们要进行很多次(成千上万次),则可以缓存无效chars数组的副本以供重用。

回答

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

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

这样做的一个好处:编写代码以修剪文件名字符串中的前导/尾随空格时要格外小心。

回答

此类清除文件名和路径;像这样使用

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

这是代码;

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

}

回答

目标文件系统也很重要。

在NTFS下,无法在特定目录中创建某些文件。
例如。 $根启动