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

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


推荐阅读

    linux命令行重命名?

    linux命令行重命名?,图片,名字,名称,软件,代码,文件,命令,文件名,批量,方面,L

    字符串查找命令linux?

    字符串查找命令linux?,系统,字符串,工具,信息,文件,命令,字符,选项,文本,范

    linux命令行模式清页?

    linux命令行模式清页?,工作,系统,命令,信息,地址,目录,内容,文件,操作,功能,l

    linux进入命令行模式?

    linux进入命令行模式?,系统,地址,情况,工作,命令,终端,首页,信息,目录,界面,l

    linux命令替换字符串?

    linux命令替换字符串?,字符串,文件,批量,首次,数据,命令,内容,方法,用字,结

    linux命令重命名配置?

    linux命令重命名配置?,图片,名称,名字,文件,软件,代码,命令,文件名,脚本,批

    linux底线模式命令?

    linux底线模式命令?,系统,档案,密码,状态,工作,命令,模式,文件,明文,界面,lin

    linux检查挂载命令?

    linux检查挂载命令?,设备,系统,信息,情况,状态,服务,软件,命令,磁盘,网络,lin

    linux启用命令模式?

    linux启用命令模式?,系统,密码,数字,首页,电脑,情况,终端,界面,模式,命令,安

    linux拼接字符串命令?

    linux拼接字符串命令?,系统,工作,代码,工具,名称,信息,地址,时间,数据,命令,l

    linux命令行模式联网?

    linux命令行模式联网?,系统,网络,地址,密码,软件,检测,信息,工具,终端,界面,l

    linux回到命令行模式?

    linux回到命令行模式?,系统,密码,状态,工具,电脑,终端,界面,环境,地方,命令,

    linux命令模式联网?

    linux命令模式联网?,网络,系统,工具,软件,密码,地址,最新,信息,工作,数据,lin

    linux进去命令模式?

    linux进去命令模式?,系统,密码,首页,终端,命令,界面,窗口,选项,桌面,用户,lin

    添加字符串命令linux?

    添加字符串命令linux?,情况,名称,文件,位置,名字,地方,连续,信息,命令,内容,L

    linux一般检查命令?

    linux一般检查命令?,网络,系统,检测,情况,工作,信息,命令,进程,时间,设备,lin

    linux命令模式全屏?

    linux命令模式全屏?,系统,工具,电脑,数据,位置,命令,虚拟机,分辨率,字符串,

    检查硬件linux命令?

    检查硬件linux命令?,信息,系统,第一,数据,设备,检测,命令,情况,灵活,实时,如

    linux命令交换文件名?

    linux命令交换文件名?,命令,文件,数据,名称,工具,地址,软件,系统,基础知识,

    linux复制命令重命名?

    linux复制命令重命名?,系统,命令,文件,目录,百度,管理,人员,设计,名字,主体,L