【问题标题】:creating files, recursively creating directories创建文件,递归创建目录
【发布时间】:2012-06-12 02:07:39
【问题描述】:

我正在阅读一些 C# 的文件 IO 教程,并设法写出了一些文件,但是如果给我的路径包含目录怎么办?

例如,我想创建名为data/my_file 的文件,但data 文件夹不存在。

线,

BinaryWriter outFile = new BinaryWriter(File.OpenWrite(path));

其中path 是上面的字符串,崩溃并显示部分路径不存在的消息,这意味着 C# 没有按要求创建它们。

我希望 C# 处理所有杂乱的目录创建和检查,而不是我必须解析路径并创建所有必要的目录。这可能吗?否则,是否有一段代码可以复制到我的项目中,它可以处理我可能忽略的任何内容(因为我对文件管理了解不多)。

【问题讨论】:

标签: c#


【解决方案1】:

System.IO.Directory.CreateDirectory() 将在指定路径中创建所有目录和子目录,如果它们不存在的话。

您可以调用它并传递路径,以确保在写入文件之前创建文件夹结构。

【讨论】:

  • 太棒了!虽然现在我有点想知道为什么这不是自动完成的。我的意思是,是否有时您不想在需要时自动创建这些目录?
  • 如果目录不存在,Microsoft 至少向 BinaryWriter 提供一个布尔参数询问您是否要创建目录,这真的很方便。我想如果你总是这样做,有人会遇到一个他们不想要这种行为的用例,并且会抱怨他们无法控制它。
  • 考虑到Directory.GetFiles()SearchOption 来指定*或递归搜索,这很奇怪。
  • 如果您不想手动解析路径来获取需要创建的目录名称,请使用Path.GetDirectoryName(string),如下所示:Directory.CreateDirectory(Path.GetDirectoryName(fullPath))。编辑:似乎H7O's answer 已经使用了这种方法,但尽管有额外的信息,但它不是公认的答案,因为它是在几个月后添加的。
【解决方案2】:

这是我通常的做法

Directory.CreateDirectory(Path.GetDirectoryName(filePath));

^ 这应该注意确保创建文件之前的所有必要文件夹(无论其中一些是否已经存在)。例如。如果你传递它“c:/a/b/c/data/my file.txt”,它应该确保创建了“c:/a/b/c/data”路径。

【讨论】:

    【解决方案3】:

    虽然System.IO.Directory.CreateDirectory() 确实会递归地为您创建目录,但我遇到了一种情况,我必须想出自己的方法。基本上,System.IO 不支持超过 260 个字符的路径,这迫使我使用 Delimon.Win32.IO 库,它适用于长路径,但不会递归创建目录。

    这是我用于递归创建目录的代码:

    void CreateDirectoryRecursively(string path)
    {
        string[] pathParts = path.Split('\\');
    
        for (int i = 0; i < pathParts.Length; i++)
        {
            if (i > 0)
                pathParts[i] = Path.Combine(pathParts[i - 1], pathParts[i]);
    
            if (!Directory.Exists(pathParts[i]))
                Directory.CreateDirectory(pathParts[i]);
        }
    }
    

    【讨论】:

    • 在超过 255 个字符的路径上,Explorer 是否会出错?这很糟糕,但我宁愿截断它,也不愿尝试处理那些废话。
    • 我知道 2 年老的颠簸,但我在修复错误时偶然发现了这个答案,并想在这个答案中添加警告。使用网络驱动器(路径以 \\ 开头)时,这将抛出“路径不是合法形式”。
    • 如果路径包含驱动器号,代码也会失败。
    • .NET 现在支持支持它们的操作系统的更长路径(例如 Windows 10 Anniversary 及更高版本)。即使可以假设代码将在受支持的操作系统上运行,我建议捕获可能的 IO 异常并提供有关目录名称长度的干净错误消息。 blogs.msdn.microsoft.com/jeremykuhne/2016/07/30/…
    【解决方案4】:

    所以,对于我来说,上述创建基本目录的效果并不好。我对此进行了一些修改,以处理驱动器号的常见情况和最后带有文件资源的路径。

        public bool CreateDirectoryRecursively(string path)
        {
            try
            {
                string[] pathParts = path.Split('\\');
                for (var i = 0; i < pathParts.Length; i++)
                {
                    // Correct part for drive letters
                    if (i == 0 && pathParts[i].Contains(":"))
                    {
                        pathParts[i] = pathParts[i] + "\\";
                    } // Do not try to create last part if it has a period (is probably the file name)
                    else if (i == pathParts.Length-1 && pathParts[i].Contains("."))
                    {
                        return true;
                    }
                    if (i > 0) { 
                        pathParts[i] = Path.Combine(pathParts[i - 1], pathParts[i]);
                    }
                    if (!Directory.Exists(pathParts[i]))
                    {
                        Directory.CreateDirectory(pathParts[i]);
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                var recipients = _emailErrorDefaultRecipients;
                var subject = "ERROR: Failed To Create Directories in " + this.ToString() + " path: " + path;
                var errorMessage = Error.BuildErrorMessage(ex, subject);
                Email.SendMail(recipients, subject, errorMessage);
                Console.WriteLine(errorMessage);
                return false;
    
            }
    
        }
    

    【讨论】:

      【解决方案5】:

      以前的答案没有处理网络路径。 附加的代码也可以处理。

      /// <summary>
      /// tests (and creates missing) directories in path containing many 
      subDirectories which might not exist.
          /// </summary>
          /// <param name="FN"></param>
          public static string VerifyPath(string FN, out bool AllOK)
          {
              AllOK = true;
              var dir = FolderUtils.GetParent(FN);
              if (!Directory.Exists(dir))//todo - move to folderUtils.TestFullDirectory
              {
                  const char DIR = '\\';
                  //string dirDel = "" + DIR;
      
                  string[] subDirs = FN.Split(DIR);
                  string dir2Check = "";
                  int startFrom = 1;//skip "c:\"
                  FN = CleanPathFromDoubleSlashes(FN);
                  if (FN.StartsWith("" + DIR + DIR))//netPath
                      startFrom = 3;//FN.IndexOf(DIR, 2);//skip first two slashes..
                  for (int i = 0; i < startFrom; i++)
                      dir2Check += subDirs[i] + DIR;//fill in begining
      
                  for (int i = startFrom; i < subDirs.Length - 1; i++)//-1 for the file name..
                  {
                      dir2Check += subDirs[i] + DIR;
                      if (!Directory.Exists(dir2Check))
                          try
                          {
                              Directory.CreateDirectory(dir2Check);
                          }
                          catch { AllOK = false; }
                  }
              }
              if (File.Exists(FN))
                  FN = FolderUtils.getFirstNonExistingPath(FN);
              if (FN.EndsWith("\\") && !Directory.Exists(FN))
                  try { Directory.CreateDirectory(FN); }
                  catch
                  {
                      HLogger.HandleMesssage("couldn't create dir:" + FN, TypeOfExceptions.error, PartsOfSW.FileStructure);
                      AllOK = false;
                  }
      
              return FN;
      
          }
      

      还有“CleanDoubleSlashes 函数”:

        public static string CleanPathFromDoubleSlashes(string basePath)
          {
              if (string.IsNullOrEmpty(basePath) || basePath.Length < 2)//don't clean first \\ of LAN address
                  return basePath;
              for (int i = basePath.Length - 1; i > 1; i--)
              {
                  if ((basePath[i] == '\\' && basePath[i - 1] == '\\') || (basePath[i] == '/' && basePath[i - 1] == '/'))
                  {
                      basePath = basePath.Remove(i, 1);//Substring(0, i - 2) + basePath.Substring(i, basePath.Length - 1 - i);
                  }
              }
              return basePath;
          }
      

      【讨论】:

      • 您提供的代码是针对 Java 而不是 C#。此外,它引用了未明确调用的第 3 方实用程序
      • 你说得对,我正在调用一些第 3 方函数 FolderUtils.getFirstNonExistingPath(FN);但代码是 C#。不是 Jave .. 我将它用于 C#... 关于功能 - 如果需要,您可以非常轻松地实现它...如果目录不存在,则循环直到 1000 结束测试...