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

How do I check if a given string is a legal/valid file name under Windows?

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


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

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

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


You can get a list of characters from Path.GetInvalidPathCharsGetInvalidFileNameChars和无效的。P></

史蒂夫:upd see how to库柏先生建议使用这些正则表达式中。P></

注:根据upd2 that the section"的言论在MSDN returned from this method is the阵列移调to contain the not characters of that are完整集的文件和目录名称错误的答案。"the details into by sixlettervaliables去提供更多。P></


for .NET 3.5 this should工作间之前:P></

正则表达式匹配should get some of the Way You。这是在System.IO.Path.InvalidPathCharsusing the片段常数;P></

1
2
3
4
5
6
7
8
9
10
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;
}

for this should。NET 3.0的工作间后:P></

msdn.microsoft.com http:/ / / /图书馆/销售额system.io.path.getinvalidpathchars(V = .aspx vs.90)P></

正则表达式匹配should get some of the Way You。这是在System.IO.Path.GetInvalidPathChars()using the片段常数;P></

1
2
3
4
5
6
7
8
9
10
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;
}

"你知道,You should also check for different formats,EG和\\server\share\dir\file.extc:\my\driveP></


尝试使用它,并捕获错误。允许的设置可能在文件系统之间或不同版本的Windows之间发生更改。换句话说,如果你想知道Windows是否喜欢这个名字,把它交给你,让它告诉你。


此类清理文件名和路径;使用方式如下

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

这是密码;

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
/// <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();
    }

}


这是我使用的:

1
2
3
4
5
6
7
8
9
    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平台的无效/非法文件名和字符。第二个是相同的,但确保该名称对于任何平台都是合法的。


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

1
2
3
"file.txt"
" file.txt"
"  file.txt"

其中一个要点是:编写从文件名字符串中删除前导/尾随空格的代码时要小心。


简化尤金·卡茨的回答:

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

1
2
3
bool IsFileNameCorrect(string fileName){
    return fileName.All(f=>!Path.GetInvalidFileNameChars().Contains(f))
}


微软:Windows内核中使用forbids the characters of范围(例如,1 - 31 - 0x1f 0x01 characters"*)& < >:?| 。尽管每个路径组件(allows NTFS目录or filename)to be up to and characters长255 paths about the characters 32767 Long,Windows内核的只supports up to 259长路径的特点。使用Windows additionally,forbids the names of the device aux MS-DOS,COM1,COM2,点美元,COM3,COM4,com5,com6,com7,com8 com9 LPT1,LPT2,,,,lpt3 lpt4 lpt5 lpt6,,,,lpt7,lpt8,lpt9 NUL和PRN,as,as these阱names(for example with any延伸,除了aux.txt),当使用长路径(前访问。nul.txt or C: ?D: 与AUX)。(事实上,may be used if an美元点延伸是提供这些限制只适用。)Windows Linux for example to,of,allows *使用"< >:?即使在NTFS | 。P></

开源的http:/ / / /维基百科en.wikipedia.org NameP></


Rather可能包括在比explicitly characters,你可以给这个regex to check for the characters of an error和报告非法,然后。你的应用ideally should the files as the user name和确切的说因为是希望只哭了犯规,如果它在stumbles an error。P></


我使用它来消除文件名中的无效字符,而不引发异常:

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

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

also,PRN,奥克斯,NUL,COM #和一些人是没有任何法律在任何目录文件名与扩展。P></


问题是如果你想确定在Windows路径路径name is a法律是法律,or if on the running is where the队列系统。我想知道更多的后者是the important,personally probably the decompose D,尽量使用全路径和_ mkdir创建目录或文件belongs茶茶在茶,然后试图创建文件。P></

这样你知道not only if the path contains only valid Windows characters,but if it represents a path that can be其实written by this process。P></


为了补充其他答案,这里有两个额外的边缘情况,您可能需要考虑。

  • 如果将工作簿保存在名称包含"["或"]"字符的文件中,Excel可能会出现问题。有关详细信息,请参阅http://support.microsoft.com/kb/215205。

  • SharePoint有一整套附加限制。有关详细信息,请参阅http://support.microsoft.com/kb/905231。


从MSDN,这是在list of characters,T是允许的:P></

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.

blockquote></blockquote></blockquote></


这是一个已经回答过的问题,但为了"其他选择",这里有一个不理想的问题:

(不理想,因为通常将异常用作流控制是一件"坏事")。

1
2
3
4
5
6
7
8
9
10
11
12
public static bool IsLegalFilename(string name)
{
    try
    {
        var fileInfo = new FileInfo(name);
        return true;
    }
    catch
    {
        return false;
    }
}


目标文件系统也很重要。

在NTFS下,无法在特定目录中创建某些文件。例如,$boot-in-root


对于这种情况,正则表达式是多余的。您可以将String.IndexOfAny()方法与Path.GetInvalidPathChars()Path.GetInvalidFileNameChars()结合使用。

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


如果在Windows10之前的环境中运行的文件名太长,那么这些答案中的许多都不起作用。同样,考虑一下您希望如何处理句点-允许前导或尾随在技术上是有效的,但是如果您不希望文件分别难以查看或删除,则可能会产生问题。

这是我为检查有效文件名而创建的验证属性。

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
32
33
34
35
36
37
38
39
40
41
42
43
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;
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[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"));
}


如果您只想检查一个包含文件名/路径的字符串是否有任何无效字符,我发现最快的方法是使用Split()将文件名分解成一个包含无效字符的部分数组。如果结果仅为1的数组,则不存在无效字符。-)

1
2
3
4
5
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;

我尝试在linqpad中的文件/路径名上运行上述方法和其他方法1000000次。

使用Split()只需约850ms。

使用Regex("[" + Regex.Escape(new string(System.IO.Path.GetInvalidPathChars())) +"]")大约需要6秒。

更复杂的正则表达式会更糟,其他一些选项也一样,比如在Path类上使用各种方法来获取文件名,并让它们的内部验证完成工作(很可能是由于异常处理的开销)。

当然,您不需要经常验证100万个文件名,所以对于这些方法中的大多数,单次迭代都是可以的。但如果你只是在寻找无效的字符,它仍然是相当有效和有效的。


我的尝试:

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

这并不完美,因为Path.GetInvalidPathChars不会返回文件名和目录名中无效的完整字符集,当然还有很多更微妙的地方。

所以我用这个方法作为补充:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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;
  }
}

它尝试创建文件,如果出现异常,则返回false。当然,我需要创建文件,但我认为这是最安全的方法。请注意,我不会删除已创建的目录。

还可以使用第一个方法进行基本验证,然后在使用路径时小心处理异常。


我是从别人那里得到这个主意的。-不知道是谁。让操作系统来完成繁重的工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
}

我建议只使用path.getfullpath()。

1
2
3
4
5
6
7
8
9
string tagetFileFullNameToBeChecked;
try
{
  Path.GetFullPath(tagetFileFullNameToBeChecked)
}
catch(AugumentException ex)
{
  // invalid chars found
}


这张支票

1
2
3
4
5
6
7
static bool IsValidFileName(string name)
{
    return
        !string.IsNullOrWhiteSpace(name) &&
        name.IndexOfAny(Path.GetInvalidFileNameChars()) < 0 &&
        !Path.GetFullPath(name).StartsWith(@"\\.");
}

筛选出包含无效字符(<>:"/\|?*和ascii 0-31)的名称,以及保留的DOS设备(CONNULCOMx。它允许前导空格和所有点名称,与Path.GetFullPath一致。(在系统上使用前导空格创建文件成功)。

已使用.NET Framework 4.7.1,在Windows 7上测试。


一个用于验证字符串中的非法字符的行程序:

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

Windows文件unrestrictive真是漂亮,我知道它可能甚至不是多好,工安问题。disallowed that are the characters are模式:WindowsP></

1
\ / : * ?" < > |

你可以写一easily expression to check if这些characters are present。更好的解决方案,虽然我在努力的摇篮和name the files to as the user wants and alert,them when a filename不粘。P></