在C#中检查文件名是否*可能*有效(不是存在)

131

在System.IO命名空间中是否有一种方法可以检查文件名的有效性?

例如,C:\foo\bar是有效的,而:“〜-*则无效。

或者稍微棘手一些,如果系统上有一个X:驱动器,则X:\foo\bar将被视为有效,但否则不会。

我想我可以自己编写这样的方法,但我更感兴趣的是内置的方法。


“不是说它存在”是否需要验证文件夹存在与否?检查的边界是什么?驱动器存在且字符全部有效吗? - BenAlabaster
14个回答

96

只需要去做;

System.IO.FileInfo fi = null;
try {
  fi = new System.IO.FileInfo(fileName);
}
catch (ArgumentException) { }
catch (System.IO.PathTooLongException) { }
catch (NotSupportedException) { }
if (ReferenceEquals(fi, null)) {
  // file name is not valid
} else {
  // file name is valid... May check for existence by calling fi.Exists.
}

要创建一个FileInfo实例,文件本身不需要存在。


17
请注意FileInfo。任何字符串,即使只是一个字母也是构造函数的有效参数,但仅仅尝试使用new FileInfo(pathTheuserEntered)将导致FileInfo假定文件相对于当前工作目录,这可能不是您想要的。 - Echilon
1
我使用了 bOk = System.IO.Path.IsPathRooted(fileName); 来增强这个解决方案,而不是 bOk = true;。 - jing
5
这段代码无法捕获包含斜杠“/”的文件名,因为这样的文件名是无效的。 - Marc
1
你可以添加一个检查,以确定你的文件名是否包含 char[] badChars = Path.GetInvalidFileNameChars() 返回的数组中的任何字符。 - Jon Dosmann
1
我在.NET 6中尝试了这个代码,并且对包含无效字符*?@"C:\Dir\Invalid*?*Characters.txt" 返回了true。我注意到文档称,仅会在 .NET Core 2.1以前的版本中因为无效字符引发ArgumentException异常。 - Simon Elms
显示剩余4条评论

37

4
这个方法返回的数组不保证包含在文件和目录名称中无效的所有字符。备注 - Jake Berger
1
让我再强调一下。仅凭字符是不足以知道它是否有效的。例如,:可以出现零次或一次,但如果存在,则它并不总是字符串的第二个字符! - Robert P
1
这不是正确的。某些名称也是被禁止的,而不仅仅是某些字符。 - Wouter Schut
"DD:\\\AAA.....AAAA"。对于您的代码来说是有效的,但它并不是一个有效路径。 - Ciccio Pasticcio

20
你可以使用 System.Uri 类。Uri 类不仅适用于 Web URL,还可以处理文件系统路径。使用 Uri.TryCreate 方法找到路径是否为根路径,然后使用 IsLoopback 属性确定 Uri 是否引用本地计算机。
以下是一个简单的方法,用于确定字符串是否为有效的、本地且具有根目录的文件路径。
public bool IsPathValidRootedLocal(String pathString) {
    Uri pathUri;
    Boolean isValidUri = Uri.TryCreate(pathString, UriKind.Absolute, out pathUri);
    return isValidUri && pathUri != null && pathUri.IsLoopback;
}

我相信这会起作用。


1
你还应该检查 Uri.Schema 是否为文件。 - drowa
2
此外,像 file:///a 这样的东西也会被您的方法接受为有效路径。 - drowa
如果路径末尾有问号,也无法正常工作。C:\\foo\\bar?? - Lion King
5
即使*?字符在文件名中是无效的,对于@"C:\Dir\Invalid*?*Characters.txt"仍会返回true。 - Simon Elms

9

您可以使用存在于System.IO命名空间中的几种方法:

Directory.GetLogicalDrives() // Returns an array of strings like "c:\"
Path.GetInvalidFileNameChars() // Returns an array of characters that cannot be used in a file name
Path.GetInvalidPathChars() // Returns an array of characters that cannot be used in a path.

建议您可以这样做:

bool IsValidFilename(string testName) {
    string regexString = "[" + Regex.Escape(Path.GetInvalidPathChars()) + "]";
    Regex containsABadCharacter = new Regex(regexString);
    if (containsABadCharacter.IsMatch(testName)) {
        return false;
    }

    // Check for drive
    string pathRoot = Path.GetPathRoot(testName);
    if (Directory.GetLogicalDrives().Contains(pathRoot)) {
        // etc
    }

    // other checks for UNC, drive-path format, etc

    return true;
}

20
不会投反对票,但你真的应该在使用他人示例代码时给予信用,特别是当它并不完全正确时。这个链接是关于如何检查Windows下给定字符串是否为合法的文件名。 - Eugene Katz
4
"regexString" 应该更像这样:string regexStringPath = "[" + Regex.Escape(new string (System.IO.Path.GetInvalidPathChars())) + "]"; - panako
关于 @"C:\\Windows"(确实是双反斜杠),这是什么意思?资源管理器显示它不是一个有效的路径,但你没有检查这个问题。 - KnorxThieus

7

我想分享一个解决方案,是从搜索到的多个答案中整合出来的。希望能够帮助到其他人。

using System;
using System.IO;
//..

public static bool ValidateFilePath(string path, bool RequireDirectory, bool IncludeFileName, bool RequireFileName = false)
{
    if (string.IsNullOrEmpty(path)) { return false; }
    string root = null;
    string directory = null;
    string filename = null;
    try
    {
        // throw ArgumentException - The path parameter contains invalid characters, is empty, or contains only white spaces.
        root = Path.GetPathRoot(path);

        // throw ArgumentException - path contains one or more of the invalid characters defined in GetInvalidPathChars.
        // -or- String.Empty was passed to path.
        directory = Path.GetDirectoryName(path);

        // path contains one or more of the invalid characters defined in GetInvalidPathChars
        if (IncludeFileName) { filename = Path.GetFileName(path); }
    }
    catch (ArgumentException)
    {
        return false;
    }

    // null if path is null, or an empty string if path does not contain root directory information
    if (String.IsNullOrEmpty(root)) { return false; }

    // null if path denotes a root directory or is null. Returns String.Empty if path does not contain directory information
    if (String.IsNullOrEmpty(directory)) { return false; }

    if (RequireFileName)
    {
        // if the last character of path is a directory or volume separator character, this method returns String.Empty
        if (String.IsNullOrEmpty(filename)) { return false; }

        // check for illegal chars in filename
        if (filename.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0) { return false; }
    }
    return true;
}

6
即使文件名是有效的,您仍然可能希望使用“touch”命令来确保用户有写的权限。
如果您不会在短时间内频繁操作大量文件,则创建空文件是一种合理的方法。
如果您真的想要更轻量级的方法,比如只检查无效字符,请将您的文件名与Path.GetInvalidFileNameChars()进行比较。

这是最简单、最好的解决方案。但在使用File.Create()之前,请确保它不存在,以防它已经存在。 - user1908746

2
尝试使用此方法,它将尝试覆盖所有可能的异常情况。它适用于几乎所有与Windows相关的路径。
/// <summary>
/// Validate the Path. If path is relative append the path to the project directory by default.
/// </summary>
/// <param name="path">Path to validate</param>
/// <param name="RelativePath">Relative path</param>
/// <param name="Extension">If want to check for File Path</param>
/// <returns></returns>
private static bool ValidateDllPath(ref string path, string RelativePath = "", string Extension = "") {
    // Check if it contains any Invalid Characters.
    if (path.IndexOfAny(Path.GetInvalidPathChars()) == -1) {
        try {
            // If path is relative take %IGXLROOT% as the base directory
            if (!Path.IsPathRooted(path)) {
                if (string.IsNullOrEmpty(RelativePath)) {
                    // Exceptions handled by Path.GetFullPath
                    // ArgumentException path is a zero-length string, contains only white space, or contains one or more of the invalid characters defined in GetInvalidPathChars. -or- The system could not retrieve the absolute path.
                    // 
                    // SecurityException The caller does not have the required permissions.
                    // 
                    // ArgumentNullException path is null.
                    // 
                    // NotSupportedException path contains a colon (":") that is not part of a volume identifier (for example, "c:\"). 
                    // PathTooLongException The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.

                    // RelativePath is not passed so we would take the project path 
                    path = Path.GetFullPath(RelativePath);

                } else {
                    // Make sure the path is relative to the RelativePath and not our project directory
                    path = Path.Combine(RelativePath, path);
                }
            }

            // Exceptions from FileInfo Constructor:
            //   System.ArgumentNullException:
            //     fileName is null.
            //
            //   System.Security.SecurityException:
            //     The caller does not have the required permission.
            //
            //   System.ArgumentException:
            //     The file name is empty, contains only white spaces, or contains invalid characters.
            //
            //   System.IO.PathTooLongException:
            //     The specified path, file name, or both exceed the system-defined maximum
            //     length. For example, on Windows-based platforms, paths must be less than
            //     248 characters, and file names must be less than 260 characters.
            //
            //   System.NotSupportedException:
            //     fileName contains a colon (:) in the middle of the string.
            FileInfo fileInfo = new FileInfo(path);

            // Exceptions using FileInfo.Length:
            //   System.IO.IOException:
            //     System.IO.FileSystemInfo.Refresh() cannot update the state of the file or
            //     directory.
            //
            //   System.IO.FileNotFoundException:
            //     The file does not exist.-or- The Length property is called for a directory.
            bool throwEx = fileInfo.Length == -1;

            // Exceptions using FileInfo.IsReadOnly:
            //   System.UnauthorizedAccessException:
            //     Access to fileName is denied.
            //     The file described by the current System.IO.FileInfo object is read-only.-or-
            //     This operation is not supported on the current platform.-or- The caller does
            //     not have the required permission.
            throwEx = fileInfo.IsReadOnly;

            if (!string.IsNullOrEmpty(Extension)) {
                // Validate the Extension of the file.
                if (Path.GetExtension(path).Equals(Extension, StringComparison.InvariantCultureIgnoreCase)) {
                    // Trim the Library Path
                    path = path.Trim();
                    return true;
                } else {
                    return false;
                }
            } else {
                return true;

            }
        } catch (ArgumentNullException) {
            //   System.ArgumentNullException:
            //     fileName is null.
        } catch (System.Security.SecurityException) {
            //   System.Security.SecurityException:
            //     The caller does not have the required permission.
        } catch (ArgumentException) {
            //   System.ArgumentException:
            //     The file name is empty, contains only white spaces, or contains invalid characters.
        } catch (UnauthorizedAccessException) {
            //   System.UnauthorizedAccessException:
            //     Access to fileName is denied.
        } catch (PathTooLongException) {
            //   System.IO.PathTooLongException:
            //     The specified path, file name, or both exceed the system-defined maximum
            //     length. For example, on Windows-based platforms, paths must be less than
            //     248 characters, and file names must be less than 260 characters.
        } catch (NotSupportedException) {
            //   System.NotSupportedException:
            //     fileName contains a colon (:) in the middle of the string.
        } catch (FileNotFoundException) {
            // System.FileNotFoundException
            //  The exception that is thrown when an attempt to access a file that does not
            //  exist on disk fails.
        } catch (IOException) {
            //   System.IO.IOException:
            //     An I/O error occurred while opening the file.
        } catch (Exception) {
            // Unknown Exception. Might be due to wrong case or nulll checks.
        }
    } else {
        // Path contains invalid characters
    }
    return false;
}

2

1
该方法返回的数组不能保证包含文件和目录名称中无效的完整字符集。备注 - Jake Berger
扩展一下RobertP上面所说的...仅使用GetInvalidPathChars本身并不是测试表示路径的字符串有效性的确切方法。此方法返回“控制”字符和“ > < | ”,这意味着该字符串(减去括号内的双引号)是有效的“C:\Folder1\Folder*2\Fol:der3”。现在打开Windows资源管理器或命令控制台,尝试创建该文件夹结构并告诉我会发生什么...您需要将路径分解为其组件,并测试每个目录组件作为有效文件名的有效卷标设计者和测试。 - PMBottas

2

这似乎不是真的,至少在Mono(例如在Unity中)是这样。我使用“Bogus\Invalid!/No:Such?File!*@#!”这个虚假路径,IsPathRooted返回true,GetFileName返回斜杠后面的部分,而FileInfo则完全未能引发异常。(但是在目录上调用GetDirectories确实会引发异常,因此也许这就是我的情况的答案。) - Joe Strout
那不起作用。 Path.GetFileName("*xxx?") 将无异常地返回 "*xxx?"。 另外,不要忘记对于新文件而言那些保留的文件名是不正确的。在 Windows 中,这些名称包括 "prn"、"con" 等。 - epox
这个答案是错误的。 - rory.ap

2

我曾经像其他人一样,使用正则表达式获得了好运。

需要记住的一件事是,至少在Windows系统中,有些文件名包含合法字符但被禁止。 我想到了几个: com、nul、prn。

我现在没有它,但我有一个考虑到这些文件名的正则表达式。 如果您想要,我可以发布它,否则我相信您可以像我一样通过谷歌找到它。

-Jay


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接