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

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

提问人:tomash 提问时间:9/15/2008 最后编辑:Luke Girvintomash 更新时间:10/1/2021 访问量:191981

问:

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

C# Windows 文件 文件系统

评论

0赞 AMissico 1/7/2015
如果您打算将任何答案与正则表达式一起使用,我建议使用静态编译的正则表达式。

答:

7赞 ConroyP 9/15/2008 #1

与其显式包含所有可能的字符,不如执行正则表达式来检查是否存在非法字符,然后报告错误。理想情况下,应用程序应完全按照用户的意愿命名文件,并且只有在偶然发现错误时才会大喊大叫。

4赞 Mark Biek 9/15/2008 #2

MSDN 中,下面是不允许的字符列表:

使用当前代码页中的几乎任何字符作为名称,包括 Unicode 字符和扩展字符集 (128–255) 中的字符,但以下字符除外:

  • 不允许使用以下保留字符: < > : " / \ | ?*
  • 不允许使用整数表示形式介于 0 到 31 之间的字符。
  • 目标文件系统不允许的任何其他字符。
102赞 Eugene Katz 9/15/2008 #3

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

更新:请参阅 Steve Cooper 关于如何在正则表达式中使用它们的建议。

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

评论

11赞 Dour High Arch 7/22/2013
这并不能回答这个问题;有许多字符串仅由无效文件名的有效字符组成(例如“....”、“CON”、数百个字符长的字符串)。
50赞 Thomas Nguyen 3/22/2014
还有人对 MS 没有为此功能提供系统级功能/API 感到失望,而不是每个开发人员都必须制定自己的解决方案?想知道这是否有很好的理由,或者只是 MS 部分的疏忽。
0赞 mmmmmmmm 10/21/2015
@High Arch:请参阅问题“在 C# 中检查文件名是否可能有效(而不是它存在)”的答案。(尽管一些聪明的人关闭了这个问题,转而支持这个问题......
0赞 Justin Poliey 9/15/2008 #4

Windows 文件名非常不受限制,因此实际上它甚至可能不是什么大问题。Windows 不允许的字符包括:

\ / : * ? " < > |

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

评论

0赞 Antimony 10/1/2012
此外,字符 <= 31 也是禁止的。
5赞 Roland Rabien 9/15/2008 #5

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

评论

1赞 Werner Henze 4/30/2013
这只是事实的一半。如果调用 CreateFile 的 unicode 版本(在文件名前加上“\\?\”),则可以使用这些名称创建文件。
0赞 Thomas Weller 10/30/2018
此声明不完整,遗漏了 LPT#
8赞 Martin Faartoft 9/15/2008 #6

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

评论

1赞 rory.ap 8/26/2017
我可以创建一个名为“CLOCK$”的文件。视窗 7。
69赞 Steve Cooper 9/15/2008 #7

对于 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/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;
}

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

评论

31赞 rao 10/19/2010
string strTheseAreInvalidFileNameChars = new string( System.IO.Path.GetInvalidFileNameChars() ) ;正则表达式 regFixFileName = new Regex(“[” + Regex.Escape(strTheseAreInvalidFileNameChars ) + “]”);
2赞 Erik Philips 12/22/2013
人们的一点研究会创造奇迹。我已经更新了帖子以反映这些变化。
1赞 Paul Hunt 4/25/2014
第二段代码无法编译。“无法从 char[] 转换为字符串
1赞 Ashkan Mobayen Khiabani 11/2/2017
+1 表示代码,但请替换为 as 现在已过时Path.GetInvalidPathChars()Path.GetInvalidFileNameChars()Path.GetInvalidPathChars()
1赞 IvanH 3/31/2020
@AshkanMobayenKhiabani:InvalidPathChars 已过时,但 GetInvalidPathChars 未过时。
6赞 kfh 9/15/2008 #8

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

这样,您不仅可以知道路径是否仅包含有效的 Windows 字符,还可以知道它是否实际表示可由此过程写入的路径。

140赞 user7116 9/15/2008 #9

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 个限制)\?\

评论

12赞 SqlRyan 4/20/2009
+1 用于包含保留文件名 - 这些在之前的答案中被遗漏了。
2赞 user9876 12/2/2009
如果您使用“\\?\”语法,“AUX”是一个完全可用的文件名。当然,不使用该语法的程序在处理它时会遇到真正的问题......(在 XP 上测试)
14赞 whywhywhy 2/19/2015
上述所有这些条件的正确正则表达式如下:Regex unspupportedRegex = new Regex("(^(PRN|AUX|NUL|CON|COM[1-9]|LPT[1-9]|(\\.+)$)(\\..*)?$)|(([\\x00-\\x1f\\\\?*:\";|/<>])+)|(([\\. ]+)", RegexOptions.IgnoreCase);
5赞 Wilky 8/21/2015
@whywhywhy我认为你在那个正则表达式中有一个额外的左括号。“(^(PRN|AUX|NUL|CON|COM[1-9]|LPT[1-9]|(\\.+)$)(\\..*)?$)|(([\\x00-\\x1f\\\\?*:\“;|/<>])+)|([\\. ]+)“对我有用。
8赞 mejdev 5/15/2016
我阅读了此答案中提到的同一篇文章,并通过实验发现 COM0 和 LPT0 也是不允许的。@dlf此文件适用于以“.”开头的文件名:^(?!^(?:PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d)(?:\..+)?$)(?:\.*?(?!\.))[^\x00-\x1f\\?*:\";|\/<>]+(?<![\s.])$
26赞 Dewayne Christensen 9/15/2008 #10

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

评论

2赞 gap 3/7/2012
这似乎是唯一一个针对所有约束进行测试的方法。为什么选择其他答案而不是这个?
5赞 Antimony 10/1/2012
@gap,因为它并不总是有效。例如,尝试访问 CON 通常会成功,即使它不是真正的文件。
4赞 Owen Blacker 10/2/2012
不过,在可能的情况下,最好避免引发异常的内存开销。
2赞 CodeLurker 7/25/2017
此外,您可能无权访问它;例如,通过写作来测试它,即使如果它确实存在或将要存在,您也可以阅读它。
0赞 arkon 9/27/2022
@OwenBlacker 这是不必要的预优化。
23赞 Scott Dorman 9/15/2008 #11

这是我使用的:

    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 平台的包含无效/非法文件名和字符的正则表达式。第二个做同样的事情,但确保该名称对任何平台都是合法的。

评论

4赞 yar_shukan 9/10/2014
sPattern 正则表达式不允许以句点字符开头的文件。但MSDN表示,“将句点指定为名称的第一个字符是可以接受的。例如,“.temp””。我会删除“\..*“ 使 .gitignore 正确的文件名:)
1赞 mejdev 5/15/2016
(我已经逐步改进了这一点,并删除了我留下的上一条评论)这个比答案的正则表达式更好,因为它允许“.gitignore”、“..asdf“,不允许使用”<“和”>“或日元符号,也不允许在末尾使用空格或句点(不允许名称仅由点组成):@"^(?!(?:PRN|AUX|CLOCK\$|NUL|CON|COM\d|LPT\d)(?:\..+)?$)[^\x00-\x1F\xA5\\?*:\"";|\/<>]+(?<![\s.])$"
0赞 magicandre1981 6/14/2016
对于我测试的所有文件,这都失败了。为 C:\Windows\System32\msxml6.dll 运行它报告 false。
0赞 Scott Dorman 6/15/2016
@magicandre1981 您只需要为其提供文件名,而不是完全限定的路径。
0赞 magicandre1981 6/15/2016
好的,但我需要检查完整路径是否有效。我现在使用了不同的解决方案。
2赞 s n 9/16/2008 #12

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

另请注意,这两种方法都会克隆内部数组并返回克隆。因此,如果您要经常这样做(成千上万次),您可以缓存无效字符数组的副本以供重用。Path.GetInvalidXXX()

19赞 Jon Schneider 9/19/2008 #13

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

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

从中得到的一个启示是:在编写从文件名字符串中修剪前导/尾随空格的代码时要小心。

24赞 Steve Cooper 8/7/2010 #14

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

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

}

评论

1赞 nawfal 6/12/2013
你的答案可能更适合这里:stackoverflow.com/questions/146134/...
2赞 Dominik Weber 8/24/2010 #15

目标文件系统也很重要。

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

评论

2赞 Christian Hayter 8/24/2010
当然,这不是由于 NTFS 命名规则,而仅仅是因为目录中已经存在调用的文件?$Boot
4赞 Joe 1/20/2012 #16

为了补充其他答案,以下是您可能需要考虑的几个其他边缘情况。

3赞 JerKimball 1/1/2013 #17

这是一个已经回答的问题,但只是为了“其他选项”,这里有一个不理想的问题:

(不理想,因为通常使用 Exceptions 作为流控制是一件“坏事”)

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

评论

0赞 tcbrazil 1/20/2015
您的示例不适用于 CON 文件 (C:\temp\CON)。
0赞 Mark A. Donohoe 11/1/2015
但是“C:\temp\CON”不是有效的文件名吗?为什么不呢?
0赞 rory.ap 8/26/2017
@MarqueIV -- 不,这是无效的。阅读上面的所有答案和评论,或自己尝试看看。
0赞 rory.ap 8/26/2017
@Jer,“/example”是不合法的,但您的方法返回 .true
0赞 Mark A. Donohoe 8/26/2017
啊啊......我错过了“CON”部分。从字符串的角度来看,名称本身是有效的(这就是我所指的),但我现在看到 CON 是一个保留名称,因此从 Windows 的角度来看它是无效的。我的错。
6赞 JoelFan 2/26/2013 #18

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

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

public static string SanitizeFileName(string fileName)
{
    return InvalidFileRegex.Replace(fileName, string.Empty);
}
0赞 Tony Sun 1/10/2017 #19

我建议只使用 Path.GetFullPath()

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

评论

0赞 ρяσѕρєя K 1/10/2017
添加一些解释和答案,说明此答案如何帮助 OP 解决当前问题
0赞 Tony Sun 4/27/2017
请参阅 MSDN 中的 AugumentExpetion 文档,其中的内容为:path 是一个长度为零的字符串,仅包含空格,或者包含 GetInvalidPathChars 中定义的一个或多个无效字符。- 或 - 系统无法检索绝对路径。
0赞 Michel Jansson 4/15/2020
从理论上讲(根据文档),这应该有效,但问题是至少在 .NET Core 3.1 中,它没有。
10赞 tmt 3/4/2017 #20

简化尤金·卡茨的回答:

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

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

评论

0赞 Jack Griffin 5/24/2018
你的意思是:“返回!fileName.Any(f=>Path.GetInvalidFileNameChars()。包含(f));“ ?
0赞 tmt 5/24/2018
@JackGriffin 答案是肯定的!感谢您的关注。
1赞 Piotr Zierhoffer 3/12/2020
虽然这段代码非常好读,但我们应该考虑到 的遗憾内部。请看这里: referencesource.microsoft.com/#mscorlib/system/io/path.cs,289 - 对于 your 的每个字符,都会创建一个数组的克隆。Path.GetInvalidFileNameCharsfileName
0赞 Ciccio Pasticcio 7/3/2020
“DD:\\\\\AAA.....AAAA”。无效,但对于您的代码,它是有效的。
0赞 Koray 8/25/2022
你真的愿意为文件名的每个字符调用 Path.GetInvalidFileNameChars() 吗?
1赞 Brent 3/14/2017 #21

如果文件名太长且在Windows 10之前的环境中运行,其中许多答案将不起作用。同样,想想你想用句点做什么 - 允许前导或尾随在技术上是有效的,但如果你不希望文件难以分别查看或删除,可能会产生问题。

这是我创建的验证属性,用于检查有效的文件名。

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

和测试

[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赞 Nick Albrecht 8/26/2017 #22

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

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 中对文件/路径名运行此方法和上面提到的其他方法 1,000,000 次。

使用时间仅为 ~850 毫秒。Split()

使用时间约为 6 秒。Regex("[" + Regex.Escape(new string(System.IO.Path.GetInvalidPathChars())) + "]")

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

当然,您不需要验证 100 万个文件名并不常见,因此对于大多数这些方法来说,一次迭代是可以的。但是,如果您只查找无效字符,它仍然非常有效。

0赞 Maxence 9/30/2017 #23

我的尝试:

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

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

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赞 KenR 10/2/2017 #24

我从某人那里得到了这个想法。- 不知道是谁。让操作系统完成繁重的工作。

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赞 Igor Levicki 8/11/2020
这应该是公认的答案(网络路径可能除外)。
0赞 Vlad 3/15/2018 #25

此检查

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

过滤掉带有无效字符(和 ASCII 0-31)的名称,以及保留的 DOS 设备 (, , )。它允许前导空格和全点名称,与 .(在我的系统上创建带有前导空格的文件成功)。<>:"/\|?*CONNULCOMxPath.GetFullPath


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

-1赞 Zananok 12/2/2018 #26

一个用于验证字符串中 illigal 字符的行:

public static bool IsValidFilename(string testName) => !Regex.IsMatch(testName, "[" + Regex.Escape(new string(System.IO.Path.InvalidPathChars)) + "]");
-1赞 Igor Levicki 8/22/2019 #27

在我看来,这个问题唯一正确的答案是尝试使用路径并让操作系统和文件系统对其进行验证。否则,您只是在重新实现(而且可能很糟糕)操作系统和文件系统已经使用的所有验证规则,如果将来更改这些规则,您将不得不更改代码以匹配它们。

评论

0赞 Hefaistos68 9/1/2022
名称可能有效,但仍然不能作为文件使用,请参阅上面的答案。
0赞 Hefaistos68 9/2/2022
我确切地知道你在说什么。但请检查 Windows 如何管理文件名(有些实际上是设备)。正如我所说,请阅读上面的评论,这些评论已经详细解释了为什么不能使用这种方法。我甚至还没有开始谈论这种方法的安全和审计问题。
0赞 Igor Levicki 9/3/2022
我从 1997 年开始使用 Windows API,所以我很清楚有些文件名实际上可能是设备。在 Linux 上也是如此,但这仍然是有效的输入和输出文件名,具体取决于用户想要/被允许做什么。您可以像使用正则表达式“验证”电子邮件地址一样“验证”文件名,但是在实际尝试之前,您不会知道是否可以创建文件/发送电子邮件。因此,这种验证是完全没有意义的,更不用说它不是可移植的,不可能涵盖所有现在和未来的文件系统。/dev/null

上一个:跟踪文件句柄

下一个:C# NetCDF 库