当路径太长时,File.Exists()错误地返回false

我目前正在开发一个遍历各种目录的程序,以确保使用File.Exists()存在特定文件。

该应用程序声称某些文件实际存在时不存在,我最近发现此错误是由于路径太长。

我发现SO上存在一些问题,它们File.Exists()返回错误的值,但似乎没有解决这个特定问题。

重命名目录和文件以缩短路径实际上不是一个选项,所以我不知道此时该怎么做。 有解决这个问题的解决办法吗?

正在使用的代码没有什么特别之处(我已经删除了一些不相关的代码),但我会在下面包含它以防万一它有所帮助。

  private void checkFile(string path) { if (!File.Exists(path)) Console.WriteLine(" * File: " + path + " does not exist."); } 

这是丑陋和低效的,但它可以解决MAX_PATH限制:

 const int MAX_PATH = 260; private static void checkPath(string path) { if (path.Length >= MAX_PATH) { checkFile_LongPath(path); } else if (!File.Exists(path)) { Console.WriteLine(" * File: " + path + " does not exist."); } } 

这是checkFile_LongPath函数:

 private static void checkFile_LongPath(string path) { string[] subpaths = path.Split('\\'); StringBuilder sbNewPath = new StringBuilder(subpaths[0]); // Build longest subpath that is less than MAX_PATH characters for (int i = 1; i < subpaths.Length; i++) { if (sbNewPath.Length + subpaths[i].Length >= MAX_PATH) { subpaths = subpaths.Skip(i).ToArray(); break; } sbNewPath.Append("\\" + subpaths[i]); } DirectoryInfo dir = new DirectoryInfo(sbNewPath.ToString()); bool foundMatch = dir.Exists; if (foundMatch) { // Make sure that all of the subdirectories in our path exist. // Skip the last entry in subpaths, since it is our filename. // If we try to specify the path in dir.GetDirectories(), // We get a max path length error. int i = 0; while(i < subpaths.Length - 1 && foundMatch) { foundMatch = false; foreach (DirectoryInfo subDir in dir.GetDirectories()) { if (subDir.Name == subpaths[i]) { // Move on to the next subDirectory dir = subDir; foundMatch = true; break; } } i++; } if (foundMatch) { foundMatch = false; // Now that we've gone through all of the subpaths, see if our file exists. // Once again, If we try to specify the path in dir.GetFiles(), // we get a max path length error. foreach (FileInfo fi in dir.GetFiles()) { if (fi.Name == subpaths[subpaths.Length - 1]) { foundMatch = true; break; } } } } // If we didn't find a match, write to the console. if (!foundMatch) { Console.WriteLine(" * File: " + path + " does not exist."); } } 

从MSDN – 命名文件,路径和命名空间 :

在Windows API中(以下段落中讨论了一些例外),路径的最大长度为MAX_PATH,定义为260个字符。

Windows API具有许多函数,这些函数也具有Unicode版本,以允许扩展长度路径,最大总路径长度为32,767个字符。 这种类型的路径由反斜杠分隔的组件组成,每个组件都取决于GetVolumeInformation函数的lpMaximumComponentLength参数中返回的值(此值通常为255个字符)。 要指定扩展长度路径,请使用"\\?\"前缀。 例如, "\\?\D:\very long path"

由于您不能将"\\?\"前缀与相对路径一起使用 ,因此相对路径始终限制为总共MAX_PATH个字符。

(重点补充)

如果所有路径都是完整路径,则可以更新代码以使用扩展长度路径说明符,如下所示:

 const longPathSpecifier = @"\\?"; private void checkFile(string path) { // Add the long-path specifier if it's missing string longPath = (path.StartsWith(longPathSpecifier) ? path : longPathSpecifier + path); if (!File.Exists(longPath)) { // Print the original path Console.WriteLine(" * File: " + path + " does not exist."); } } 

更新:

对于文件I / O,路径字符串的“\?\”前缀告诉Windows API禁用所有字符串解析并将其后面的字符串直接发送到文件系统。 例如, 如果文件系统支持大路径和文件名 ,则可以超过Windows API强制执行的MAX_PATH限制。

至少在我的系统上(使用Windows 7),不支持长文件名,因此我无法validation上述解决方案是否适合您。

更新:我找到了一个可行的解决方案,但它相当丑陋。 这是我在伪代码中所做的:

  1. 将路径拆分为目录数组
  2. 获取路径中长度小于260个字符的最长部分(MAX_PATH)。
  3. 为路径的该部分创建DirectoryInfo (“dir”以供将来参考)。
  4. 对于路径中的其余目录:
    一个。 调用dir.GetDirectories()并检查结果中是否包含下一个目录
    湾 如果是这样,将dir设置为该DirectoryInfo并继续挖掘
    C。 如果没有,则该路径不存在
  5. 一旦我们浏览了导致我们文件的所有目录,就调用dir.GetFiles()并查看我们的文件是否存在于返回的FileInfo对象中。

我自己从来没有遇到这个问题,另一个SOpost的人建议打开文件的句柄,从而首先避免整个“存在”检查。 不确定这是否还有“长文件名”问题:

这是第二个答案:

检查文件/目录是否存在:有更好的方法吗?

不确定这是否有用:P

您需要P / Invoke Win32 API才能使其正常工作:

  [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] static extern uint GetFileAttributes(string lpFileName); public static bool DirectoryExists(string path) { uint attributes = GetFileAttributes(path.StartsWith(@"\\?\") ? path : @"\\?\" + path); if (attributes != 0xFFFFFFFF) { return ((FileAttributes)attributes).HasFlag(FileAttributes.Directory); } else { return false; } } public static bool FileExists(string path) { uint attributes = GetFileAttributes(path.StartsWith(@"\\?\") ? path : @"\\?\" + path); if (attributes != 0xFFFFFFFF) { return !((FileAttributes)attributes).HasFlag(FileAttributes.Directory); } else { return false; } }