【问题标题】:Error while trying to rename files using C#尝试使用 C# 重命名文件时出错
【发布时间】:2021-09-14 01:58:55
【问题描述】:

我有以下代码将以下树中的文件从 00000001.pdf 重命名为具有这 8 个字符左填充的最后一个文件,例如:00000100.pdf

  1. 文件夹 1
    • 子文件夹 1
      1. 子文件夹1
        • pdffile1
        • pdffile2
      2. 子文件夹2
        • pdffile3
        • pdffile4
    • 子文件夹2
      1. 子文件夹3
        • pdffile5
        • pdffile6

但由于某些原因,在其中一些子文件夹中,它不断地重命名它们。 有时它只是跳转到另一个数字,就好像它是一个异步操作一样。但是,如果我停止并重新开始,直到第二个下一个文件夹,它会再次出现问题。 但是这个错误只发生在 19 个文件夹中。 确实,他们的 pdf 名称与其他名称不同,但我看不出它是如何相关的。 其他文件的名称类似于 "DOCUMENT_01" 等等,但它们是: 0000000100000001.pdf 0000000200000001.pdf 0000000300000001.pdf 等等

static void Main(string[] args)
        {
            Console.WriteLine("Digite a pasta 'pai' onde serão buscados pdfs dentro das pastas 'filhas':");
            string path = Console.ReadLine();
            foreach (string dir in Directory.EnumerateDirectories(path))
            {

                foreach (string subdir in Directory.EnumerateDirectories(dir))
                {
                    Console.WriteLine($"{dir} - {subdir}");
                    int n = 1;
                    foreach (string pdffile in Directory.EnumerateFiles(subdir, "*.pdf", SearchOption.AllDirectories))
                    {                                  
                        Console.WriteLine(n.ToString().PadLeft(8, '0') + "  " + new FileInfo(pdffile).Length);
                        File.Move(pdffile, subdir + $"\\{n.ToString().PadLeft(8, '0')}.pdf");
                        n++;

                    }
                    Console.WriteLine("\n\n");
                }
            }
        }

可能出了什么问题? 它应该等待 File.Move 方法结束以添加 n + 1,然后作为同步操作移动到下一个 pdffile。那么为什么它会在随机时间后跳跃数字以及为什么它会一直持续下去呢? 只是要记住,如果我停止程序并重新启动并将被搞砸的文件夹作为第一个文件夹,它就可以了,只有当它进入下一个文件夹时,或者它开始给我的下一个文件夹之后又是这个错误。

希望我能说清楚...感谢您的关注!

编辑:将尝试使用 FileInfo 类为我提供带有 SearchOption.AllDirectories 选项的父文件夹,并排除这个 3 阶段循环加上实际上适用于任何类型的树结构 EDIT2:尝试过,作为“树独立”脚本工作,但在第一个文件夹之后的文件名得到相同的结果......因为它真的很快,在 3 秒内它从 00000169.pdf00006239.pdf 在一个只有 330 个项目的文件夹中。

【问题讨论】:

  • Directory.EnumerateFiles(dir) 在枚举时如果目录内容发生变化会不高兴。您应该通过附加 ToList: Directory.EnumerateFiles(dir).ToList() 来实现集合
  • 哦,我会试试的!谢谢!但为什么只在这些特定文件夹中?我在另一个 +/-30 个文件夹中使用错误,但没有发生错误
  • 可能是文件大小需要更长时间才能移动,我认为 File.Move 不会等待操作完成。
  • 它工作了@KlausGütter!丹克塞尔!感谢@Matt,奇怪的行为,但至少我不必再处理它了!
  • 在您最后一次编辑之后,您的问题是什么以及解决方案是什么都不清楚了。您可能希望将解决方案添加为答案,而不是通过编辑问题。

标签: c# foreach rename


【解决方案1】:

Klaus Gütter 帮助我回答,我只是将 .ToList() 添加到 Directory.EnumerateFiles 中,所以它首先制作了一个固定列表,然后为每个文件制作了 foreach

它将重命名文件夹中的每个pdf及其子文件夹

            Console.WriteLine("Type the folder you want to start with:");
            string path = Console.ReadLine();
            string dir = "";
            int n = 1;
            foreach (string pdffile in Directory.EnumerateFiles(path, "*.pdf", SearchOption.AllDirectories).ToList())
            {

                FileInfo fi = new FileInfo(pdffile);
                if (fi.DirectoryName == dir)
                {
                    Console.WriteLine("\t" + n.ToString().PadLeft(8, '0'));
                    File.Move(pdffile, dir + $"\\{n.ToString().PadLeft(8, '0')}.pdf");
                    n++;
                }

                else
                {
                    n = 1;
                    dir = fi.DirectoryName;
                    Console.WriteLine("\n\n" + dir);
                    File.Move(pdffile, dir + $"\\{n.ToString().PadLeft(8, '0')}.pdf");
                    Console.WriteLine("\t" + n.ToString().PadLeft(8, '0'));
                    n++;

                }

            }

【讨论】:

    【解决方案2】:

    正如已经评论的那样,移动或重命名文件“同时”代码正在枚举这些文件的列表,就像发布的代码似乎做的那样,这不是一个好主意。这会导致明显的问题,您应该以某种方式简单地标记文件,然后再回来重命名或移动它们。

    更重要的是,与重命名/移动文件相关的大问题与您对当前问题的描述完全相同。问题是错误不稳定且不一致。使其非常难以追踪。但是,您描述的问题是在枚举这些文件时移动/重命名文件的经典商标。

    话虽如此,在给定起始文件夹的情况下遍历未知数量的文件夹级别的最佳和最简单的方法是使用递归。在很多情况下,可以通过一些很好的 out 循环来避免递归,但是当我们不知道有多少级别的文件夹时,使用简单的循环或foreach 循环范例可能是可行的,但是,你很可能会添加变量和代码,只会使这变得更复杂。这在当前代码中显示,并添加了dir 变量以跟踪“何时”使用不同的文件夹。递归非常适合这种情况。

    在这种情况下,将从给定的“起始”文件夹位置为每个文件夹和子文件夹调用一次此递归方法。这意味着每次调用此递归方法时都会开始处理不同的文件夹。所以n 总是从 1 开始,我们不需要跟踪当前文件夹路径。

    因此,此方法的签名将采用DirectoryFolder 对象作为“起始”文件夹。首先我们创建一些变量;一个FileInfo 数组pdffiles 保存给定文件夹中的pdf文件;除了 DirectoryInfo 数组 foldersInThisFolder 来保存此起始文件夹中的所有其他文件夹。最后一个int n 来索引文件,就像发布的代码正在做的那样。

    接下来我们在这个“开始”文件夹中获取所有的 pdf 文件。如果此文件夹中有 pdf 文件,那么我们会遍历这些文件并对其进行处理。接下来,我们在这个“开始”文件夹中获取所有其他文件夹。然后开始循环遍历每个文件夹。对于此集合中的每个文件夹,我们将使用下一个文件夹作为“开始”文件夹递归调用此方法,然后整个过程继续进行,直到遍历这些文件夹结束。

    static void TraverseDirectoryTree(DirectoryInfo startingFolder) {
      FileInfo[] pdffiles = null;
      DirectoryInfo[] foldersInThisFolder = null;
      int n = 1;
      Console.WriteLine(startingFolder.FullName);
      // get all the pdf files in this folder
      try {
        pdffiles = startingFolder.GetFiles("*.pdf");
      }
      catch (Exception e) {
        // you may want to catch specific exceptions
        // however in this example we do not care what
        // the exception is, we will simply ignore this.
        // in most cases pdffiles will be null if an exception is thrown
        Console.WriteLine(e.Message);
      }
      if (pdffiles != null) {
        foreach (FileInfo pdffile in pdffiles) {
          Console.WriteLine(pdffile.FullName + " -> " + n.ToString().PadLeft(8, '0') + "  " + pdffile.Length);
          //File.Move(pdffile.FullName, pdffile.DirectoryName + $"\\{n.ToString().PadLeft(8, '0')}.pdf");
          // add file path to a list of files to rename later?
          n++;
        }
        // start over wiith the sub folders in this folder
        foldersInThisFolder = startingFolder.GetDirectories();
        foreach (DirectoryInfo dirInfo in foldersInThisFolder) {
          TraverseDirectoryTree(dirInfo);
        }
      }
    }
    

    用法……

    Console.WriteLine("Type the folder you want to start with:");
    string path = Console.ReadLine();
    DirectoryInfo di = new DirectoryInfo(path);
    TraverseDirectoryTree(di);
    

    编辑...经过进一步测试,您似乎想要做的只是“重命名”pdf文件。正如所建议的,一个简单的解决方案是保存我们要重命名的文件,然后,在我们收集要重命名的文件之后,我们只需遍历这些文件并重命名它们。这应该可以通过在枚举文件集合时重命名文件来消除任何问题。

    为了提供帮助,我创建了一个名为 filesToRenameDictionary<string, int>。在递归遍历所有文件夹时,我们将添加要重命名的每个 pdf 文件的完整路径作为 Key,并将 int 值 n 作为 Value。字典填满后,我们只需遍历它并重命名文件。

    private static Dictionary<string, int> filesToRename = new Dictionary<string, int>();
    

    然后替换递归方法中注释掉的行TraverseDirectoryTree...

    //File.Move(pdffile.FullName, pdffile.DirectoryName + $"\\{n.ToString().PadLeft(8, '0')}.pdf");
    

    随着……

    filesToRename.Add(pdffile.FullName, n);
    

    然后在字典被填满后,我们会循环遍历它并重命名文件,比如……

    DirectoryInfo di = new DirectoryInfo(path);
    TraverseDirectoryTree(di);
    foreach (KeyValuePair<string, int> kvp in filesToRename) {
      int index = kvp.Key.ToString().LastIndexOf(@"\");
      string dir = kvp.Key.ToString().Substring(0, index);
      File.Move(kvp.Key, dir + $"\\{kvp.Value.ToString().PadLeft(8, '0')}.pdf");
    }
    

    我希望这是有道理的……

    【讨论】:

    • 首先非常非常非常感谢你为我开设的这个“c#课”呵呵呵呵我明白了很多!我还将检查仅将 .ToList() 添加到 Directory.EnumerateFiles(dir) 是否具有更好的性能。但是我明白了很多!我将发布对我有用的最终代码作为答案,因为它也有效。再次感谢!
    • 对不起,我没有 15 声望来投票,但它说它被记录了,呵呵
    • 如果你只是保存pdf的完整文件名和路径,那么你将不得不编写代码来解析哪个文件在哪个目录中。换句话说,您有一个文件和路径列表,但是,您不会知道哪个文件是 000001.pdf、000002.pdf 或 000003.pdf 等……,而无需添加代码来解析每个文件属于哪个文件夹。如果您“保存”每个文件的 pdf 索引(1、2、3),那么您不必跟踪每个 pdf 文件属于哪个文件夹。
    猜你喜欢
    • 2023-03-18
    • 2020-12-26
    • 2013-07-30
    • 1970-01-01
    • 1970-01-01
    • 2014-01-26
    • 1970-01-01
    • 2019-01-04
    • 1970-01-01
    相关资源
    最近更新 更多