迭代文件夹和子文件夹的最佳方法

46

如何最好地迭代文件夹和子文件夹以获取指定位置开始的每个文件夹中文件大小、文件总数和文件夹总大小?


只有两个词:foreach和递归。 - Andrew Orsich
1
@Bugai13 - 这是一个很好的计算机科学作业建议,但是 .Net 框架已经包含了这个功能。另外,从迭代集合/可枚举对象转向查询集合/可枚举对象,甚至让集合/可枚举对象自己完成工作,是在现代环境下解决该问题的正确方式。 - Ritch Melton
1
请修复标题中的拼写错误:迭代(iterate),并将第一个字母大写。 - HuBeZa
@Pekka,起初我想探索不同的方法,但我想我会坚持使用C#。 - Rod
6个回答

59

如果你使用的是.NET 4,你可能希望使用System.IO.DirectoryInfo.EnumerateDirectoriesSystem.IO.DirectoryInfo.EnumerateFiles方法。如果你像其他帖子建议的那样使用Directory.GetFiles方法,该方法调用将不会返回直到检索到所有条目为止。如果你正在使用递归,这可能需要很长时间。

文档中可以得知:

EnumerateFilesGetFiles方法的区别如下:

  • 当你使用EnumerateFiles时,你可以在整个集合被返回之前开始枚举FileInfo对象的集合。
  • 当你使用GetFiles时,必须等待整个FileInfo对象数组返回后才能访问数组。

因此,在处理许多文件和目录时,EnumerateFiles可以更高效。


2
不得不跳过使用GetDirectory()的数十个示例才找到这一个。对于超过50k个目录,它太慢了。感谢这个难得的例子,救了我的一天。 - krowe2

47

使用Directory.GetFiles()。该页面底部包括一个完全递归的示例。

注意:如果使用.NET 4及以上版本,请使用下面Chris Dunaway的答案进行更现代化的处理。

// For Directory.GetFiles and Directory.GetDirectories
// For File.Exists, Directory.Exists
using System;
using System.IO;
using System.Collections;

public class RecursiveFileProcessor 
{
    public static void Main(string[] args) 
    {
        foreach(string path in args) 
        {
            if(File.Exists(path)) 
            {
                // This path is a file
                ProcessFile(path); 
            }               
            else if(Directory.Exists(path)) 
            {
                // This path is a directory
                ProcessDirectory(path);
            }
            else 
            {
                Console.WriteLine("{0} is not a valid file or directory.", path);
            }        
        }        
    }

    // Process all files in the directory passed in, recurse on any directories 
    // that are found, and process the files they contain.
    public static void ProcessDirectory(string targetDirectory) 
    {
        // Process the list of files found in the directory.
        string [] fileEntries = Directory.GetFiles(targetDirectory);
        foreach(string fileName in fileEntries)
            ProcessFile(fileName);

        // Recurse into subdirectories of this directory.
        string [] subdirectoryEntries = Directory.GetDirectories(targetDirectory);
        foreach(string subdirectory in subdirectoryEntries)
            ProcessDirectory(subdirectory);
    }
    
    // Insert logic for processing found files here.
    public static void ProcessFile(string path) 
    {
        Console.WriteLine("Processed file '{0}'.", path);       
    }
}

1
一个注意点:当目录中包含大量文件(数万到数十万)时,Directory.GetFiles() 可能会非常慢。在这些情况下,我迄今发现的最快方法实际上是启动一个进程来运行 dir 命令并重定向输出并解析它(或将其管道传输到文件并读取该文件)。当然,除非我预计单个目录中有 50,000+ 文件,否则我不会考虑这种方法。 - quentin-starin
实际上,使用P/Invoke调用FindFirstFile等函数可能比使用dir命令更快,尽管我相信.NET最终也会转换为这种方式。 - Lloyd

8
要遍历所有目录、子文件夹和文件,无论有多少个子文件夹和文件。
string [] filenames;
 fname = Directory.GetFiles(jak, "*.*", SearchOption.AllDirectories).Select(x => Path.GetFileName(x)).ToArray();

然后通过循环或您想要的方式,您可以从数组中获取所需内容。


1
如果您的文件夹结构中存在循环,这将导致无限循环,请参见http://msdn.microsoft.com/en-us/library/ms143448.aspx。 - Anthony Wieser

5
这里有一个使用Peter上述建议和递归的示例。
using System;
using System.IO;

namespace FileSystemUtils
{
    class Program
    {
        static void Main(string[] args)
        {
            string folderPath = "C:\\docs";

            DirectoryInfo startDir = new DirectoryInfo(folderPath);

            RecurseFileStructure recurseFileStructure = new RecurseFileStructure();
            recurseFileStructure.TraverseDirectory(startDir);
        }

        public class RecurseFileStructure
        {
            public void TraverseDirectory(DirectoryInfo directoryInfo)
            {
                var subdirectories = directoryInfo.EnumerateDirectories();

                foreach (var subdirectory in subdirectories)
                {
                    TraverseDirectory(subdirectory);
                }

                var files = directoryInfo.EnumerateFiles();

                foreach (var file in files)
                {
                    HandleFile(file);
                }
            }

            void HandleFile(FileInfo file)
            {
                Console.WriteLine("{0}", file.Name);
            }
        }
    }
}

1
请注意你需要进行验证检查。
string[] fileNames = Directory.GetFiles("c:\\", "*.*", SearchOption.AllDirectories);
int fileCount = fileNames.Count();
long fileSize = fileNames.Select(file => new FileInfo(file).Length).Sum(); // in bytes

什么类型的验证检查 - Rod
@rod:检查是否存在Directory.Exists(否则,DirectoryNotFoundException)。我想不到其他什么了,但也许我漏掉了些什么。 - HuBeZa

1

要遍历文件和文件夹,通常会使用 DirectoryInfoFileInfo 类型。FileInfo 类型具有返回文件大小(以字节为单位)的 Length 属性。

我认为你必须编写自己的代码来遍历文件并计算总文件大小,但这应该是一个相当简单的递归函数。


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