在控制深度的同时遍历目录 - C#

Walking through directory while controling depth - C#

我需要能够从目录和子目录中获取所有文件,但我想让用户可以选择子目录的深度。 即,不仅仅是当前目录或所有目录,他应该能够选择 1、2、3、4 目录等的深度。

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

如有任何帮助,我们将不胜感激。

谢谢, 大卫

这是我当前的代码(我发现 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);
        }

    }

使用可以确定每个递归调用的最大深度变量,然后你不能 return 一旦达到所需的深度。

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);
    }

}

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

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

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();

现在我注意到你在数 MasterFileCounter & MasterFolderCounter

你可以很容易地写:

    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());
    }
}