繁体   English   中英

C#使用EnumerateDirectories列出所有“leaf”子目录

[英]C# List all “leaf” subdirectories with EnumerateDirectories

大家早上好,我有一个包含数千个不同深度子目录的文件夹。 我需要列出所有不包含子目录的目录(众所周知的“行尾”)。 如果它们包含文件就没问题。 有没有办法用EnumerateDirectories做到这一点?

例如,如果返回完全递归的EnumerateDirectories:

/files/
/files/q
/files/q/1
/files/q/2
/files/q/2/examples
/files/7
/files/7/eb
/files/7/eb/s
/files/7/eb/s/t

我只对以下内容感兴趣:

/files/q/1
/files/q/2/examples
/files/7/eb/s/t

这应该工作:

var folderWithoutSubfolder = Directory.EnumerateDirectories(root, "*.*", SearchOption.AllDirectories)
     .Where(f => !Directory.EnumerateDirectories(f, "*.*", SearchOption.TopDirectoryOnly).Any());

如果你想避免为每个目录调用两次EnumerateDirectories() ,你可以像这样实现它:

public IEnumerable<string> EnumerateLeafFolders(string root)
{
    bool anySubfolders = false;

    foreach (var subfolder in Directory.EnumerateDirectories(root))
    {
        anySubfolders = true;

        foreach (var leafFolder in EnumerateLeafFolders(subfolder))
            yield return leafFolder;
    }

    if (!anySubfolders)
        yield return root;
}

我做了一些时序测试,对我而言,这种方法的速度是使用Linq方法的两倍多。

我使用发布版本运行此测试,在任何调试器之外运行。 我在包含大量文件夹的SSD上运行它 - LEAF文件夹的总数为25035。

我对程序的第二次运行的结果(第一次运行是预热磁盘缓存):

Calling Using linq.  1 times took 00:00:08.2707813
Calling Using yield. 1 times took 00:00:03.6457477
Calling Using linq.  1 times took 00:00:08.0668787
Calling Using yield. 1 times took 00:00:03.5960438
Calling Using linq.  1 times took 00:00:08.1501002
Calling Using yield. 1 times took 00:00:03.6589386
Calling Using linq.  1 times took 00:00:08.1325582
Calling Using yield. 1 times took 00:00:03.6563730
Calling Using linq.  1 times took 00:00:07.9994754
Calling Using yield. 1 times took 00:00:03.5616040
Calling Using linq.  1 times took 00:00:08.0803573
Calling Using yield. 1 times took 00:00:03.5892681
Calling Using linq.  1 times took 00:00:08.1216921
Calling Using yield. 1 times took 00:00:03.6571429
Calling Using linq.  1 times took 00:00:08.1437973
Calling Using yield. 1 times took 00:00:03.6606362
Calling Using linq.  1 times took 00:00:08.0058955
Calling Using yield. 1 times took 00:00:03.6477621
Calling Using linq.  1 times took 00:00:08.1084669
Calling Using yield. 1 times took 00:00:03.5875057

如您所见,使用yield方法要快得多。 (可能是因为它没有枚举每个文件夹两次。)

我的测试代码:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;

namespace Demo
{
    class Program
    {
        private void run()
        {
            string root = "F:\\TFROOT";

            Action test1 = () => leafFolders1(root).Count();
            Action test2 = () => leafFolders2(root).Count();

            for (int i = 0; i < 10; ++i)
            {
                test1.TimeThis("Using linq.");
                test2.TimeThis("Using yield.");
            }
        }

        static void Main()
        {
            new Program().run();
        }

        static IEnumerable<string> leafFolders1(string root)
        {
            var folderWithoutSubfolder = Directory.EnumerateDirectories(root, "*.*", SearchOption.AllDirectories)
                 .Where(f => !Directory.EnumerateDirectories(f, "*.*", SearchOption.TopDirectoryOnly).Any());

            return folderWithoutSubfolder;
        }

        static IEnumerable<string> leafFolders2(string root)
        {
            bool anySubfolders = false;

            foreach (var subfolder in Directory.EnumerateDirectories(root))
            {
                anySubfolders = true;

                foreach (var leafFolder in leafFolders2(subfolder))
                    yield return leafFolder;
            }

            if (!anySubfolders)
                yield return root;
        }
    }

    static class DemoUtil
    {
        public static void Print(this object self)
        {
            Console.WriteLine(self);
        }

        public static void Print(this string self)
        {
            Console.WriteLine(self);
        }

        public static void Print<T>(this IEnumerable<T> self)
        {
            foreach (var item in self)
                Console.WriteLine(item);
        }

        public static void TimeThis(this Action action, string title, int count = 1)
        {
            var sw = Stopwatch.StartNew();

            for (int i = 0; i < count; ++i)
                action();

            Console.WriteLine("Calling {0} {1} times took {2}",  title, count, sw.Elapsed);
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM