如何在C#中递归列出目录中的所有文件?

406

如何在C#中递归列出目录和子目录中的所有文件?


7
问题在于,如果您无法访问单个目录,则这很容易损坏:没有结果... - Marc Gravell
3
如果在访问某些文件时遇到问题,请查看“枚举文件抛出异常”(Enumerating Files Throwing Exception)页面(https://dev59.com/WlzUa4cB1Zd3GeqP0zIr)。 - CodesInChaos
24个回答

534

请注意,在 .NET 4.0 中,有基于迭代器而非数组的文件函数(据说)内置:

foreach (string file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
{
    Console.WriteLine(file);
}

目前我会使用以下代码;内置的递归方法如果您无法访问单个子目录,则很容易崩溃...; Queue<string> 的使用避免了过多的调用堆栈递归,迭代器块避免了我们拥有一个巨大的数组。

static void Main() {
    foreach (string file in GetFiles(SOME_PATH)) {
        Console.WriteLine(file);
    }
}

static IEnumerable<string> GetFiles(string path) {
    Queue<string> queue = new Queue<string>();
    queue.Enqueue(path);
    while (queue.Count > 0) {
        path = queue.Dequeue();
        try {
            foreach (string subDir in Directory.GetDirectories(path)) {
                queue.Enqueue(subDir);
            }
        }
        catch(Exception ex) {
            Console.Error.WriteLine(ex);
        }
        string[] files = null;
        try {
            files = Directory.GetFiles(path);
        }
        catch (Exception ex) {
            Console.Error.WriteLine(ex);
        }
        if (files != null) {
            for(int i = 0 ; i < files.Length ; i++) {
                yield return files[i];
            }
        }
    }
}

考虑空格作为目录名称 => http://stackoverflow.com/q/5368054/2336304 - SerG
12
所有想知道 *.* 是否包括没有文件扩展名的文件的人都可以了解:是的,它包括,我刚刚进行了测试。 - Tobias Knauss

231

这篇文章包含了你需要的一切。但是与其搜索文件并比较名称,不如只打印出名称。

可以按以下方式进行修改:

static void DirSearch(string sDir)
{
    try
    {
        foreach (string d in Directory.GetDirectories(sDir))
        {
            foreach (string f in Directory.GetFiles(d))
            {
                Console.WriteLine(f);
            }
            DirSearch(d);
        }
    }
    catch (System.Exception excpt)
    {
        Console.WriteLine(excpt.Message);
    }
}

由barlop添加

GONeale提到上面的代码没有列出当前目录中的文件,并建议将文件列表部分放在获取目录部分的外面。以下代码可以实现这个功能。它还包括一条Writeline语句可以取消注释,用于跟踪递归过程中的位置,有助于展示递归调用的方式。

            DirSearch_ex3("c:\\aaa");
            static void DirSearch_ex3(string sDir)
            {
                //Console.WriteLine("DirSearch..(" + sDir + ")");
                try
                {
                    Console.WriteLine(sDir);

                    foreach (string f in Directory.GetFiles(sDir))
                    {
                        Console.WriteLine(f);
                    }

                    foreach (string d in Directory.GetDirectories(sDir))
                    {
                        DirSearch_ex3(d);
                    }
                }
                catch (System.Exception excpt)
                {
                    Console.WriteLine(excpt.Message);
                }
            }

88
该方法不列出初始目录下的文件,只列出其子目录及更低级别的文件。我建议将GetFiles方法移到GetDirectories方法之外。 - GONeale
1
有时候,人们不想要初始目录中的文件,这种情况下对于相对较小的结构来说是完美的。对于非常大的列表,请使用类似Marc Gravell解决方案的东西:https://dev59.com/PHNA5IYBdhLWcg3wgeSk#929418 - Joseph Gabriel
2
@GONeale是正确的。对于用户不希望看到输入根目录的文件列表是不太可能的。这里关键是输入这个词。它被输入是有原因的。 - Florin Mircea
2
我不得不在内部的foreach循环周围添加一个try catch,否则它会因为访问被拒绝的错误而无法继续。 - Shaun Vermaak
3
避免捕捉异常 - 例如,你真的想捕捉一个OutOfMemoryException吗?只拦截你能够处理的异常。 - alastairtree
显示剩余2条评论

163
Directory.GetFiles("C:\\", "*.*", SearchOption.AllDirectories)

2
如何避免登录用户没有访问某些文件夹的错误。 - Romil Kumar Jain
6
@Romil,我不认为这段代码片段试图展示完整的功能性,只是展示了原始的功能,这是问题提出者所寻求的。感谢分享,Pescuma! - kayleeFrye_onDeck
@kayleeFrye_onDeck,我只是提出一个问题,以防在获取文件时出现任何文件夹的问题。由于这个问题,我们实现了自定义递归函数。 - Romil Kumar Jain
4
使用这个解决方案会出现“UnauthorizedAccessException”的错误。你需要一个能够处理此类错误的解决方案。 - Kairan
1
对于所有目录 Directory.GetDirectories(@"yourpath" , "*.*", SearchOption.AllDirectories) - Soner from The Ottoman Empire

25
最短唱片
string[] files = Directory.GetFiles(@"your_path", "*.jpg", SearchOption.AllDirectories);

4
这句话的意思是:这不应该是 string[] files 吗? - Philipp Lenssen
2
我不明白为什么这个答案首先被添加,以及为什么它有那么多赞。这与6年前添加的https://dev59.com/PHNA5IYBdhLWcg3wgeSk#23253890完全相同。如果您想添加字符串声明和@以避免需要转义斜杠,为什么不改进旧答案呢? - Arvo Bowen

16
在 .NET 4.5 中,至少有这个版本,它更短,并且具有评估任何文件标准以包含在列表中的附加奖励:
public static IEnumerable<string> GetAllFiles(string path, 
                                              Func<FileInfo, bool> checkFile = null)
{
    string mask = Path.GetFileName(path);
    if (string.IsNullOrEmpty(mask)) mask = "*.*";
    path = Path.GetDirectoryName(path);
    string[] files = Directory.GetFiles(path, mask, SearchOption.AllDirectories);

    foreach (string file in files)
    {
        if (checkFile == null || checkFile(new FileInfo(file)))
            yield return file;
    }
}

使用方法如下:

var list = GetAllFiles(mask, (info) => Path.GetExtension(info.Name) == ".html").ToList();

这段代码没有处理空目录的情况... 函数内部没有返回语句。 - FrumkinWY
@FrumkinWY 空目录会发生什么?我现在没有可用的机器来测试。 - John Kaster

14
IEnumerable<string> GetFilesFromDir(string dir) =>
 Directory.EnumerateFiles(dir).Concat(
 Directory.EnumerateDirectories(dir)
          .SelectMany(subdir => GetFilesFromDir(subdir)));

5

以下是如何获取子目录的FileInfo文件:

var dir = new DirectoryInfo(rootPath);
FileInfo[] files = dir.GetFiles("*.*", SearchOption.AllDirectories);

3
在2.0框架中,您可以使用以下代码(此代码列出根文件夹中的文件,是最受欢迎的答案):

static void DirSearch(string dir)
{
    try
    {
        foreach (string f in Directory.GetFiles(dir))
            Console.WriteLine(f);
        foreach (string d in Directory.GetDirectories(dir))
        {
            Console.WriteLine(d);
            DirSearch(d);
        }

    }
    catch (System.Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

3
一些很好的答案,但这些答案没有解决我的问题。
一旦出现文件夹权限问题: "Permission Denied",代码就会失败。这是我用来解决 "Permission Denied" 问题的方法:
private int counter = 0;

    private string[] MyDirectories = Directory.GetDirectories("C:\\");

    private void ScanButton_Click(object sender, EventArgs e)
    {
        Thread MonitorSpeech = new Thread(() => ScanFiles());
        MonitorSpeech.Start();
    }

    private void ScanFiles()
    {
        string CurrentDirectory = string.Empty;

        while (counter < MyDirectories.Length)
        {
            try
            {
                GetDirectories();
                CurrentDirectory = MyDirectories[counter++];
            }
            catch
            {
                if (!this.IsDisposed)
                {
                    listBox1.Invoke((MethodInvoker)delegate { listBox1.Items.Add("Access Denied to : " + CurrentDirectory); });
                }
            }
        }
    }

    private void GetDirectories()
    {
        foreach (string directory in MyDirectories)
        {
            GetFiles(directory);
        }
    }

    private void GetFiles(string directory)
    {
        try
        {
            foreach (string file in Directory.GetFiles(directory, "*"))
            {
                listBox1.Invoke((MethodInvoker)delegate { listBox1.Items.Add(file); });
            }
        }
        catch
        {
            listBox1.Invoke((MethodInvoker)delegate { listBox1.Items.Add("Access Denied to : " + directory); });
        }
    }

希望这能帮助其他人。

3
一个简单清晰的解决方案
/// <summary>
/// Scans a folder and all of its subfolders recursively, and updates the List of files
/// </summary>
/// <param name="sFullPath">Full path of the folder</param>
/// <param name="files">The list, where the output is expected</param>
internal static void EnumerateFiles(string sFullPath, List<FileInfo> fileInfoList)
{
    try
    {
        DirectoryInfo di = new DirectoryInfo(sFullPath);
        FileInfo[] files = di.GetFiles();

        foreach (FileInfo file in files)
            fileInfoList.Add(file);

        //Scan recursively
        DirectoryInfo[] dirs = di.GetDirectories();
        if (dirs == null || dirs.Length < 1)
            return;
        foreach (DirectoryInfo dir in dirs)
            EnumerateFiles(dir.FullName, fileInfoList);

    }
    catch (Exception ex)
    {
        Logger.Write("Exception in Helper.EnumerateFiles", ex);
    }
}

4
你目前正在手动完成DirectoryInfo.GetFiles()会自动完成的工作,只需使用SearchOption.AllDirectories重载即可,它将自动递归所有文件夹。因此,这是一个复杂的解决方案。 - philw

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