【问题标题】:Walking through directory while controling depth - C#在控制深度的同时遍历目录 - C#
【发布时间】:2015-07-06 11:45:06
【问题描述】:

我需要能够从一个目录和子目录中获取所有文件,但我想给用户选择子目录深度的选项。 即,不只是当前目录或所有目录,而且他应该能够选择 1,2,3,4 目录等的深度。

我见过很多遍历目录树的示例,但似乎都没有解决这个问题。就个人而言,我对递归感到困惑......(我目前使用的)。我不确定如何在递归函数期间跟踪深度。

任何帮助将不胜感激。

谢谢, 大卫

这是我当前的代码(我找到了here):

    static void FullDirList(DirectoryInfo dir, string searchPattern, string excludeFolders, int maxSz, string depth)
    {

        try
        {
            foreach (FileInfo file in dir.GetFiles(searchPattern))
            {

                if (excludeFolders != "")
                    if (Regex.IsMatch(file.FullName, excludeFolders, RegexOptions.IgnoreCase)) continue;

                myStream.WriteLine(file.FullName);
                MasterFileCounter += 1;

                if (maxSz > 0 && myStream.BaseStream.Length >= maxSz)
                {
                    myStream.Close();
                    myStream = new StreamWriter(nextOutPutFile());
                }

            }
        }
        catch
        {
            // make this a spearate streamwriter to accept files that failed to be read.
            Console.WriteLine("Directory {0}  \n could not be accessed!!!!", dir.FullName);
            return;  // We alredy got an error trying to access dir so dont try to access it again
        }

        MasterFolderCounter += 1;

        foreach (DirectoryInfo d in dir.GetDirectories())
        {
            //folders.Add(d);
            // if (MasterFolderCounter > maxFolders) 
            FullDirList(d, searchPattern, excludeFolders, maxSz, depth);
        }

    }

【问题讨论】:

  • stackoverflow.com/questions/6141648/… 可能会给你一个提示
  • 注意递归?将参数添加到递归函数,在每个子文件夹调用时递增。这样你就知道自己有多深了。
  • @Thorarins,谢谢!我不敢相信我没有找到 - 我搜索了很多! - 我正在研究它,并将回帖

标签: c# getfiles


【解决方案1】:

使用可以在每次递归调用时递减的 maxdepth 变量,然后您不能在达到所需深度后返回。

static void FullDirList(DirectoryInfo dir, string searchPattern, string excludeFolders, int maxSz, int maxDepth)
{

    if(maxDepth == 0)
    {
        return;
    }

    try
    {
        foreach (FileInfo file in dir.GetFiles(searchPattern))
        {

            if (excludeFolders != "")
                if (Regex.IsMatch(file.FullName, excludeFolders, RegexOptions.IgnoreCase)) continue;

            myStream.WriteLine(file.FullName);
            MasterFileCounter += 1;

            if (maxSz > 0 && myStream.BaseStream.Length >= maxSz)
            {
                myStream.Close();
                myStream = new StreamWriter(nextOutPutFile());
            }

        }
    }
    catch
    {
        // make this a spearate streamwriter to accept files that failed to be read.
        Console.WriteLine("Directory {0}  \n could not be accessed!!!!", dir.FullName);
        return;  // We alredy got an error trying to access dir so dont try to access it again
    }

    MasterFolderCounter += 1;

    foreach (DirectoryInfo d in dir.GetDirectories())
    {
        //folders.Add(d);
        // if (MasterFolderCounter > maxFolders) 
        FullDirList(d, searchPattern, excludeFolders, maxSz, depth - 1);
    }

}

【讨论】:

  • 而不是传递depthmaxDepth参数,只需传递maxDepth,每次递归减1并检查它是否> 0
  • 改为使用@TimRogers 建议
  • 大家好,感谢您的回复。我已经尝试过了,但它似乎没有工作。我正在调查它 - 很快就会回复。 - 谢谢!
  • 好吧,对不起,伙计们——它工作得很好。当涉及到递归时,我总是感到困惑......但现在它非常简单!非常感谢!
【解决方案2】:

让我们从重构代码开始,使其工作更容易理解。

因此,这里的关键练习是递归返回与所需模式匹配的所有文件,但仅限于一定深度。让我们先获取这些文件。

public static IEnumerable<FileInfo> GetFullDirList(
    DirectoryInfo dir, string searchPattern, int depth)
{
    foreach (FileInfo file in dir.GetFiles(searchPattern))
    {
        yield return file;
    }

    if (depth > 0)
    {
        foreach (DirectoryInfo d in dir.GetDirectories())
        {
            foreach (FileInfo f in GetFullDirList(d, searchPattern, depth - 1))
            {
                yield return f;
            }
        }
    }
}

这只是简化了文件递归的工作。

但是您会注意到它没有根据excludeFolders 参数排除文件。让我们现在解决这个问题。让我们开始构建FullDirList

第一行是

    var results =
        from fi in GetFullDirList(dir, searchPattern, depth)
        where String.IsNullOrEmpty(excludeFolders)
            || !Regex.IsMatch(fi.FullName, excludeFolders, RegexOptions.IgnoreCase)
        group fi.FullName by fi.Directory.FullName;

这会获取所有文件,将它们限制为excludeFolders,然后按它们所属的文件夹对所有文件进行分组。我们这样做是为了接下来可以这样做:

    var directoriesFound = results.Count();
    var filesFound = results.SelectMany(fi => fi).Count();

现在我注意到你在数 MasterFileCounterMasterFolderCounter

你可以很容易地写:

    MasterFolderCounter+= results.Count();
    MasterFileCounter += results.SelectMany(fi => fi).Count();

现在,要写出这些文件,您似乎正在尝试将文件名聚合到单独的文件中,但要保持文件的最大长度 (maxSz)。

这是如何做到这一点的:

    var aggregateByLength =
        results
            .SelectMany(fi => fi)
            .Aggregate(new [] { new StringBuilder() }.ToList(),
                (sbs, s) =>
                {
                    var nl = s + Environment.NewLine;
                    if (sbs.Last().Length + nl.Length > maxSz)
                    {
                        sbs.Add(new StringBuilder(nl));
                    }
                    else
                    {
                        sbs.Last().Append(nl);
                    }
                    return sbs;
                });

现在编写文件变得非常简单:

    foreach (var sb in aggregateByLength)
    {
        File.WriteAllText(nextOutPutFile(), sb.ToString());
    }

所以,完整的事情变成了:

static void FullDirList(
    DirectoryInfo dir, string searchPattern, string excludeFolders, int maxSz, int depth)
{
    var results =
        from fi in GetFullDirList(dir, searchPattern, depth)
        where String.IsNullOrEmpty(excludeFolders)
            || !Regex.IsMatch(fi.FullName, excludeFolders, RegexOptions.IgnoreCase)
        group fi.FullName by fi.Directory.FullName;

    var directoriesFound = results.Count();
    var filesFound = results.SelectMany(fi => fi).Count();

    var aggregateByLength =
        results
            .SelectMany(fi => fi)
            .Aggregate(new [] { new StringBuilder() }.ToList(),
                (sbs, s) =>
                {
                    var nl = s + Environment.NewLine;
                    if (sbs.Last().Length + nl.Length > maxSz)
                    {
                        sbs.Add(new StringBuilder(nl));
                    }
                    else
                    {
                        sbs.Last().Append(nl);
                    }
                    return sbs;
                });

    foreach (var sb in aggregateByLength)
    {
        File.WriteAllText(nextOutPutFile(), sb.ToString());
    }
}

【讨论】:

  • 哇!这是什么语言!! ☺ - 感谢您的工作和详细的解释。我将尝试学习这一点并理解您所写的内容。 - 这比以前的方法快/慢吗?
  • @DaveyD - 除非您有数十万个文件,否则您可能不会发现速度差异。我的代码应该更易于维护,因为它拆分了递归代码并线性化了迭代代码。哦,是的,它是c#。 ?
  • 好的,谢谢 - 说话更像是 2-3000。我确实喜欢您的代码设置,非常好。我还在努力弄清楚你写了什么!这看起来有点像 sql....
  • @DaveyD - results 部分使用 LINQ,类似于 SQL。你以前用过 LINQ 吗?
  • 我明白了。不,我以前从未使用过 linq。看起来很有趣,功能强​​大且有用!!。我也不太了解sql。我确实知道一点要四处走动,但远非舒适。
猜你喜欢
  • 2012-12-03
  • 2018-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多