【问题标题】:Recurse a folder in order using IEnumerable<string>使用 IEnumerable<string> 按顺序递归文件夹
【发布时间】:2018-08-13 05:23:36
【问题描述】:

我编写了一个函数来递归文件夹结构,但它没有按预期工作。预期的行为是返回传递的根文件夹的文件,然后进入第一个文件夹,返回这些文件,然后进入第一个文件夹的第一个子文件夹并列出这些文件,等等。

相反,我设法让它首先返回所有 1 级文件夹,然后是所有 2 级文件夹,然后是所有 3 级文件夹等。我可以看到有什么问题,但我不确定如何解决它。

我怎样才能重写它以获得我预期的行动方案?

static IEnumerable<string> recurseFolder(String Folder)
{
    if (Directory.Exists(Folder))
    {
        string[] files = null;
        string[] dirs = null;
        try { files = Directory.GetFiles(Folder); } catch (Exception) { }

        if (files != null)
        {
            foreach (var item in files)
            {
                yield return item;
            }
        }

        try { dirs = Directory.GetDirectories(Folder); } catch (Exception) { }

        if (dirs != null)
        {
            foreach (var dir in dirs)
            {
                foreach (var item in recurseFolder(dir))
                {
                    yield return item;
                }

            }
        }
    }

}

更新 1 这可能与 Linux 和 Windows 上的 dotnet core 不一致,这是我在 Windows 上的结果,也是我期望的结果。

d:\root\Folder A\File 1.txt
d:\root\Folder A\Folder B\File 2.txt
d:\root\Folder A\Folder B\Folder C\File 3.txt
d:\root\Folder A\Folder B\Folder D\File 4.txt
d:\root\Folder A\Folder E\File 5.txt
d:\root\Folder A\Folder F\File 6.txt
d:\root\Folder A\Folder F\Folder G\File 7.txt
d:\root\Folder H\File 8.txt
d:\root\Folder H\Folder I\File 9.txt
d:\root\Folder H\Folder I\Folder J\File 10.txt
d:\root\Folder H\Folder I\Folder K\File 11.txt
d:\root\Folder H\Folder L\File 12.txt
d:\root\Folder M\File 13.txt

这是我在 Linux 上的结果

/home/x/Root/Folder A/File 1.txt
/home/x/Root/Folder A/Folder E/File 5.txt
/home/x/Root/Folder A/Folder B/File 2.txt
/home/x/Root/Folder A/Folder B/Folder D/File 4.txt
/home/x/Root/Folder A/Folder B/Folder C/File 3.txt
/home/x/Root/Folder A/Folder F/File 6.txt
/home/x/Root/Folder A/Folder F/Folder G/File 7.txt
/home/x/Root/Folder H/File 8.txt
/home/x/Root/Folder H/Folder I/File 9.txt
/home/x/Root/Folder H/Folder I/Folder K/File 11.txt
/home/x/Root/Folder H/Folder I/Folder J/File 10.txt
/home/x/Root/Folder H/Folder L/File 12.txt
/home/x/Root/Folder M/File 13.txt

也许我的代码正在运行,但我只需要手动按字母顺序对目录进行排序,我想知道 linux 是否以创建或修改的日期顺序返回它们。

【问题讨论】:

  • 您可能想知道Directory.GetFiles 有一个需要SearchOption 的重载,您可以告诉它搜索所有子目录。 msdn.microsoft.com/en-us/library/ms143316(v=vs.110).aspx
  • 为什么不将 SearchOption.AllDirectoreis 传递给 GetFiles 中的递归目录?您是否试图避免您无权访问的文件夹,或者试图列出C:C:\Windows 中的所有文件?
  • 避免捕获异常 - 例如,您真的想捕获 OutOfMemoryException 吗?
  • 有一个静态的List&lt;&gt; 然后获取所有文件名Directory.GetFiles 并获取所有文件夹名称Directory.GetDirectories 然后为每个文件夹再次调用相同的方法。继续在静态列表中插入文件名
  • @donggas90 有时用例子解释比用描述误解更容易。

标签: c# .net-core


【解决方案1】:

我已经有一段时间没有使用它了,但如果没记错的话,我相信它可能会给你想要的结果。它使用平面递归。

这是 JustDecompile 从 VB 的粗略转换,因此希望语法正确。

Recurser.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;

namespace Recurser
{
    public class Recurser
    {
        public Files Files
        {
            get;
        }

        public Folders Folders
        {
            get;
        }

        public string RootFolder
        {
            get;
        }

        public string SearchPattern
        {
            get;
        }

        public Recurser(string RootFolder) : this(RootFolder, "*.*")
        {
        }

        public Recurser(string RootFolder, string SearchPattern)
        {
            this.Folders = new Folders();
            this.Files = new Files();
            this.SearchPattern = SearchPattern;
            this.RootFolder = RootFolder;
        }

        public void Start()
        {
            this.Folders.Clear();
            this.Files.Clear();
            int iCount = 0;
            if (Directory.Exists(this.RootFolder))
            {
                this.Folders.Add(new DirectoryInfo(this.RootFolder));
                while (iCount < this.Folders.Count)
                {
                    this.Folders.AddRange(this.Folders[iCount].GetDirectories());
                    this.Files.AddRange(this.Folders[iCount].GetFiles());
                    iCount = checked(iCount + 1);
                }
            }
        }
    }
}

Folders.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;

namespace Recurser
{
    public class Folders : List<DirectoryInfo>
    {
        public Folders()
        {
        }

        public override string ToString()
        {
            base.Sort(new FolderComparer());
            StringBuilder stringBuilder = new StringBuilder();
            base.ForEach((DirectoryInfo Folder) => stringBuilder.AppendLine(Folder.FullName));
            return stringBuilder.ToString();
        }

        public IEnumerable<string> ToStrings()
        {
            base.Sort(new FolderComparer());
            return this.Select((DirectoryInfo Folder) => Folder.fullName);
        }
    }
}

Files.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;

namespace Recurser
{
    public class Files : List<FileInfo>
    {
        public Files()
        {
        }

        public override string ToString()
        {
            base.Sort(new FileComparer());
            StringBuilder stringBuilder = new StringBuilder();
            base.ForEach((FileInfo File) => stringBuilder.AppendLine(File.FullName));
            return stringBuilder.ToString();
        }

        public IEnumerable<string> ToStrings()
        {
            base.Sort(new FileComparer());
            return this.Select((FileInfo File) => File.fullName);
        }
    }
}

FolderComparer.cs

using System;
using System.Collections.Generic;
using System.IO;

namespace Recurser
{
    public class FolderComparer : IComparer<DirectoryInfo>
    {
        public FolderComparer()
        {
        }

        public int Compare(DirectoryInfo X, DirectoryInfo Y)
        {
            return X.FullName.CompareTo(Y.FullName);
        }
    }
}

FileComparer.cs

using System;
using System.Collections.Generic;
using System.IO;

namespace Recurser
{
    public class FileComparer : IComparer<FileInfo>
    {
        public FileComparer()
        {
        }

        public int Compare(FileInfo X, FileInfo Y)
        {
            return X.FullName.CompareTo(Y.FullName);
        }
    }
}

【讨论】:

  • 哇,这非常全面,也许我应该更具体一些,但是我想使用 Yield 即时返回结果,因为它是一个非常大的文件夹/文件结构,我想制作它立即开始,不要花很长时间在手前构建结构。
  • @Matt — 看来你是对的; Linux 必须以不同于FullName 的顺序返回它们。您可以运行一个小测试来确认这一点。假设是这样,您唯一的防御措施就是在构建整个列表后对其进行排序——这当然意味着放弃yield。所以必须给出一些东西:恐怕您的排序要求或yield。如果线程阻塞是一个严重的问题,你可以使用async/await
【解决方案2】:

事实证明,由于我对该问题的理解不正确,因此修复非常简单,我只是在文件和 dir 数组中添加了一些 OrderBy 语句。

static IEnumerable<string> recurseFolder(String Folder)
{
    if (Directory.Exists(Folder))
    {
        string[] files = null;
        string[] dirs = null;
        try { files = Directory.GetFiles(Folder); } catch (Exception) { }

        if (files != null)
        {
            foreach (var item in files.OrderBy(o => o))
            {
                yield return item;
            }
        }

        try { dirs = Directory.GetDirectories(Folder); } catch (Exception) { }

        if (dirs != null)
        {
            foreach (var dir in dirs.OrderBy(o =>o))
            {
                foreach (var item in recurseFolder(dir))
                {
                    yield return item;
                }
            }
        }
    }
}

【讨论】:

    猜你喜欢
    • 2011-08-19
    • 1970-01-01
    • 2011-11-07
    • 1970-01-01
    • 1970-01-01
    • 2018-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多