C# C“全部列出”;“叶”;带有枚举目录的子目录

C# C“全部列出”;“叶”;带有枚举目录的子目录,c#,directory,enumerate,C#,Directory,Enumerate,大家早上好, 我有一个文件夹,其中包含数千个不同深度的子目录。我需要列出所有不包含子目录的目录(俗称“行尾”)。如果它们包含文件就可以了。有没有办法用枚举目录来实现这一点 例如,如果返回一个完全递归的枚举目录: /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

大家早上好, 我有一个文件夹,其中包含数千个不同深度的子目录。我需要列出所有不包含子目录的目录(俗称“行尾”)。如果它们包含文件就可以了。有没有办法用枚举目录来实现这一点

例如,如果返回一个完全递归的枚举目录:

/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;
}
如您所见,使用收益率方法要快得多。(可能是因为它不会枚举每个文件夹两次。)

我的测试代码:

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);
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用系统诊断;
使用System.IO;
使用System.Linq;
名称空间演示
{
班级计划
{
私家车
{
string root=“F:\\TFROOT”;
Action test1=()=>leafFolders1(root.Count();
Action test2=()=>leafFolders2(root.Count();
对于(int i=0;i<10;++i)
{
test1.TimeThis(“使用linq”);
测试2.TimeThis(“使用收益率”);
}
}
静态void Main()
{
新程序().run();
}
静态IEnumerableFolders1(字符串根)
{
var folderWithoutSubfolder=Directory.EnumerateDirectories(根“***”,SearchOption.AllDirectories)
.Where(f=>!Directory.EnumerateDirectory(f,“**”,SearchOption.TopDirectoryOnly).Any());
返回folderWithoutSubfolder;
}
静态IEnumerableFolders2(字符串根)
{
bool anySubfolders=false;
foreach(目录中的var子文件夹。枚举目录(根))
{
anySubfolders=true;
foreach(leafFolders2(子文件夹)中的var leafFolder)
返回文件夹;
}
如果(!anySubfolders)
产量回归根;
}
}
静态类DemoUtil
{
公共静态无效打印(此对象本身)
{
控制台写入线(self);
}
公共静态无效打印(此字符串本身)
{
控制台写入线(self);
}
公共静态无效打印(此IEnumerable self)
{
foreach(自身中的var项目)
控制台写入线(项目);
}
公共静态void TimeThis(此操作,字符串标题,int count=1)
{
var sw=Stopwatch.StartNew();
对于(int i=0;i
EnumerateDirectory是惰性计算的,因此在Tim的回答中进行的额外调用非常便宜。当我将您的代码与Tim的代码进行基准测试时,Tim的运行时间略少于一半。我想这是因为递归使用迭代器会增加很多开销。@Brian您是否多次运行测试以消除第一次运行时磁盘缓存造成的工件?是的。在开始之前,我运行了几百次测试,使用优化进行编译,并在启动计时器之前运行每个测试一次(以避免抖动瑕疵)。@Brian我发现我的方法的运行速度是原来的两倍多。我将附加我的测试代码,以便您可以看到它。您是否确保在调试器之外运行它(否则即使编译为发行版,它也将在调试模式下运行)我尝试更改您的代码以不使用递归(请参阅)。当我对它进行基准测试时,它的运行速度比Tim快10%。如果我添加了对Directory.EnumerateDirectories的额外调用,这些好处大部分不会消失,所以我认为它们与LINQ开销有关。无论如何,在您的系统上,对枚举目录的无关调用可能会更昂贵。
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);
        }
    }
}