C# 在控制depth-C时遍历目录#
我需要能够从一个目录和子目录获取所有文件,但我想让用户选择子目录的深度。 也就是说,不仅仅是当前目录或所有目录,他还应该能够选择深度为1,2,3,4的目录等 我见过许多遍历目录树的例子,但没有一个能解决这个问题。就我个人而言,我对递归感到困惑。。。(我目前正在使用)。我不确定在递归函数中如何跟踪深度 任何帮助都将不胜感激 谢谢, 大卫 这是我当前的代码(我找到的):C# 在控制depth-C时遍历目录#,c#,getfiles,C#,Getfiles,我需要能够从一个目录和子目录获取所有文件,但我想让用户选择子目录的深度。 也就是说,不仅仅是当前目录或所有目录,他还应该能够选择深度为1,2,3,4的目录等 我见过许多遍历目录树的例子,但没有一个能解决这个问题。就我个人而言,我对递归感到困惑。。。(我目前正在使用)。我不确定在递归函数中如何跟踪深度 任何帮助都将不胜感激 谢谢, 大卫 这是我当前的代码(我找到的): 使用可以在每次递归调用中递减的maxdepth变量,一旦达到所需的深度,就不能返回 static void FullDirList
使用可以在每次递归调用中递减的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);
}
}
使用可以在每次递归调用中递减的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);
}
}
让我们从重构代码开始,使其工作更容易理解 因此,这里的关键练习是递归地返回与所需模式匹配的所有文件,但只返回一定深度的文件。让我们先去拿那些文件
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
范围内,然后按它们所属的文件夹对所有文件进行分组。我们这样做是为了下一步可以这样做:
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());
}
}
让我们从重构代码开始,使其工作更容易理解 因此,这里的关键练习是递归地返回与所需模式匹配的所有文件,但只返回一定深度的文件。让我们先去拿那些文件
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
范围内,然后按它们所属的文件夹对所有文件进行分组。我们这样做是为了下一步可以这样做:
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());
}
}
可能会给你一个提示递归?将参数添加到递归函数中,在每次子文件夹调用时递增该参数。这样你就知道你有多深了。@Thorarins,谢谢!我简直不敢相信我没有找到它——我找了好多我现在正在研究它,并将发回可能会给你一个提示递归?将参数添加到递归函数中,在每次子文件夹调用时递增该参数。这样你就知道你有多深了。@Thorarins,谢谢!我简直不敢相信我没有找到它——我找了好多我现在正在研究它,将发布back,而不是传递
depth
和maxDepth
参数,只需传递maxDepth
,在每个递归上递减1,并检查它是否被更改为使用@TimRogers suggestionHi伙计们,谢谢你们的回复。我已经试过了,但似乎不起作用。我正在调查-很快就会发回。-谢谢好吧,对不起,伙计们,它工作得很好。当涉及到递归时,我总是感到困惑。。。但现在它很简单!谢谢!不要传递depth
和maxDepth
参数,只需传递maxDepth
,在每个递归上递减1,并检查它是否已更改为0以使用@TimRogers suggestionHi伙计们,谢谢你们的回复。我已经试过了,但似乎不起作用。我正在调查-很快就会发回。-谢谢好吧,对不起,伙计们,它工作得很好。当涉及到递归时,我总是感到困惑。。。但现在它很简单!谢谢!哇!这是什么语言!!☺ - 感谢您的工作和详细的解释。我将努力学习这一点,理解你写的东西这比前一种方法快/慢吗?@DaveyD-除非你有数十万个文件,否则你可能不会发现速度差异。我的代码应该更易于维护,因为它将递归代码分离出来,并将迭代代码线性化。哦,是的,是c。好的,谢谢-说得更像2-3000。我很喜欢你的代码设置,非常好。我还在想你写了什么!这看起来有点像sql…@DaveyD-results
部分使用的是LINQ,类似于sql。你以前用过LINQ吗?我明白。不,我以前从未用过linq。看起来很有趣,功能强大而且有用!!。我也不太懂sql。我确实知道一点,但是一点也不舒服。哇!这是什么语言!!☺ - 感谢您的工作和详细的解释。我将努力学习这一点,理解你写的东西这比前一种方法快/慢吗?@DaveyD-除非你有数十万个文件,否则你可能不会发现速度差异。我的代码应该更易于维护,因为它将递归代码分离出来,并将迭代代码线性化。哦,是的,是c。好的,谢谢-说得更像2-3000。我很喜欢你的代码设置,非常好。我还在想你写了什么!这看起来有点像sql…@DaveyD-results
部分使用的是LINQ,类似于sql。你以前用过LINQ吗?我明白。不,我以前从未用过linq。看起来很有趣,功能强大而且有用!!。我也不太懂sql