衡量递归搜索的进度
Measure progress in recursive search
我有一个递归搜索,它在整个目录中搜索具有指定名称的文件。
这需要相当长的时间,我想显示一个进度条,它至少显示一些进度(我不在乎进度是否仅通过文件或目录的数量或它们的实际大小来衡量)。
以下(递归代码)执行搜索:
private void searchFolder(PortableDeviceFolder parent, ref List<PortableDeviceFolder> result, string filename)
{
foreach (var item in parent.Files)
{
if (item is PortableDeviceFolder)
{
searchFolder((PortableDeviceFolder)item, ref result, filenames);
}
else if ((String.Compare(item.Name, filename)==0))
{
result.Add(parent);
Console.WriteLine("Found file in: " + parent.Name);
}
}
}
我不知道如何获取进度。我在想一个简单的算法,它可以为每个文件和文件夹测量相同的百分比;
- 文件 (10%)
- 私有(10%/3)
- 工作(10%/3)
- 项目 1 (10%/3/2)
- 项目 2 (10%/3/2)
- 家庭 (10%/3)
- ...
等等。
不幸的是,我无法在递归搜索中实现它。
有人有时间给我举个例子吗?
提前致谢
评论者TaW的建议,你可以统计目录作为进度的基础,这个建议很好。但是有一个小问题,不是建议本身,而是您的代码似乎实现的方式。
从你的问题中不清楚 PortableDeviceFolder
是什么。但它似乎抽象了文件和文件夹之间的区别,return 都来自 Files
属性。鉴于处理过程中最耗时的方面可能是给定目录的文件名的实际检索,那么如果 PortableDeviceFolder
必须 return 给定目录中的目录的唯一机制是Files
属性,您将不得不枚举所有文件以及目录,忽略为生成计数而编辑的文件 return。
换句话说,获取计数所花费的时间几乎与实际搜索给定名称所花费的时间一样长。
因此,为了这个答案的目的,我将假设 PortableDeviceFolder
class 有另一个 属性 Folders
,其中 return 是 只是目录本身。有了这样的属性,那么我们就可以利用TaW提供的建议了。
首先,您需要计算一下。这样做的方法如下所示:
private int CountFolders(PortableDeviceFolder rootFolder)
{
return rootFolder.Folders.Select(folder => CountFolders(folder)).Sum() + 1;
}
在显示任何进度之前会稍有延迟,因为当然没有有用的方法来预测上述需要多长时间。但它应该相对简短,因为我们只查看目录而不是所有文件。
为了能够在处理过程中更新进度,我们应该 运行 在单独的线程中搜索并使用 Dispatcher.Invoke()
更新 ProgressBar.Value
属性.搜索方法如下所示:
private void SearchFolder(PortableDeviceFolder parent,
List<PortableDeviceFolder> result, string fileName, IProgress<int> progress)
{
foreach (var item in parent.Files)
{
PortableDeviceFolder folder = item as PortableDeviceFolder;
if (folder != null)
{
SearchFolder(folder, result, fileName, progress);
}
else if (item.Name.Equals(fileName, StringComparison.OrdinalIgnoreCase))
{
result.Add(parent);
}
}
progress.Report(1);
}
注意:我不清楚您为什么要通过引用传递 result
参数。这似乎不需要,所以我将其更改为常规按值参数。
在您的 UI 代码中,您可以这样称呼它:
private async void SearchFolder_Click(object sender, RoutedEventArgs e)
{
Button button = (Button)sender;
button.IsEnabled = false;
string searchPath = textBlock1.Text, searchText = textBox1.Text;
List<PortableDeviceFolder> folders = new List<PortableDeviceFolder>();
PortableDeviceFolder rootFolder = new WindowsDirectoryFolder(searchPath);
progressBar1.Value = 0;
progressBar1.Maximum = await Task.Run(() => CountFolders(rootFolder));
Progress<int> progress =
new Progress<int>(increment => progressBar1.Value += increment);
await Task.Run(() => SearchFolder(rootFolder, folders, searchText, progress));
listBox1.ItemsSource = folders;
button.IsEnabled = true;
}
请注意,这里我们异步执行 CountFolders()
和 SearchFolder()
方法,以确保 UI 在工作完成时保持响应。
以上是我为演示该技术而编写的一个简单的 WPF 程序,但它可以毫无困难地适应 Winforms 或其他 GUI 框架。基本思想保持不变。
我有一个递归搜索,它在整个目录中搜索具有指定名称的文件。 这需要相当长的时间,我想显示一个进度条,它至少显示一些进度(我不在乎进度是否仅通过文件或目录的数量或它们的实际大小来衡量)。
以下(递归代码)执行搜索:
private void searchFolder(PortableDeviceFolder parent, ref List<PortableDeviceFolder> result, string filename)
{
foreach (var item in parent.Files)
{
if (item is PortableDeviceFolder)
{
searchFolder((PortableDeviceFolder)item, ref result, filenames);
}
else if ((String.Compare(item.Name, filename)==0))
{
result.Add(parent);
Console.WriteLine("Found file in: " + parent.Name);
}
}
}
我不知道如何获取进度。我在想一个简单的算法,它可以为每个文件和文件夹测量相同的百分比;
- 文件 (10%)
- 私有(10%/3)
- 工作(10%/3)
- 项目 1 (10%/3/2)
- 项目 2 (10%/3/2)
- 家庭 (10%/3)
- ...
等等。
不幸的是,我无法在递归搜索中实现它。 有人有时间给我举个例子吗? 提前致谢
评论者TaW的建议,你可以统计目录作为进度的基础,这个建议很好。但是有一个小问题,不是建议本身,而是您的代码似乎实现的方式。
从你的问题中不清楚 PortableDeviceFolder
是什么。但它似乎抽象了文件和文件夹之间的区别,return 都来自 Files
属性。鉴于处理过程中最耗时的方面可能是给定目录的文件名的实际检索,那么如果 PortableDeviceFolder
必须 return 给定目录中的目录的唯一机制是Files
属性,您将不得不枚举所有文件以及目录,忽略为生成计数而编辑的文件 return。
换句话说,获取计数所花费的时间几乎与实际搜索给定名称所花费的时间一样长。
因此,为了这个答案的目的,我将假设 PortableDeviceFolder
class 有另一个 属性 Folders
,其中 return 是 只是目录本身。有了这样的属性,那么我们就可以利用TaW提供的建议了。
首先,您需要计算一下。这样做的方法如下所示:
private int CountFolders(PortableDeviceFolder rootFolder)
{
return rootFolder.Folders.Select(folder => CountFolders(folder)).Sum() + 1;
}
在显示任何进度之前会稍有延迟,因为当然没有有用的方法来预测上述需要多长时间。但它应该相对简短,因为我们只查看目录而不是所有文件。
为了能够在处理过程中更新进度,我们应该 运行 在单独的线程中搜索并使用 Dispatcher.Invoke()
更新 ProgressBar.Value
属性.搜索方法如下所示:
private void SearchFolder(PortableDeviceFolder parent,
List<PortableDeviceFolder> result, string fileName, IProgress<int> progress)
{
foreach (var item in parent.Files)
{
PortableDeviceFolder folder = item as PortableDeviceFolder;
if (folder != null)
{
SearchFolder(folder, result, fileName, progress);
}
else if (item.Name.Equals(fileName, StringComparison.OrdinalIgnoreCase))
{
result.Add(parent);
}
}
progress.Report(1);
}
注意:我不清楚您为什么要通过引用传递 result
参数。这似乎不需要,所以我将其更改为常规按值参数。
在您的 UI 代码中,您可以这样称呼它:
private async void SearchFolder_Click(object sender, RoutedEventArgs e)
{
Button button = (Button)sender;
button.IsEnabled = false;
string searchPath = textBlock1.Text, searchText = textBox1.Text;
List<PortableDeviceFolder> folders = new List<PortableDeviceFolder>();
PortableDeviceFolder rootFolder = new WindowsDirectoryFolder(searchPath);
progressBar1.Value = 0;
progressBar1.Maximum = await Task.Run(() => CountFolders(rootFolder));
Progress<int> progress =
new Progress<int>(increment => progressBar1.Value += increment);
await Task.Run(() => SearchFolder(rootFolder, folders, searchText, progress));
listBox1.ItemsSource = folders;
button.IsEnabled = true;
}
请注意,这里我们异步执行 CountFolders()
和 SearchFolder()
方法,以确保 UI 在工作完成时保持响应。
以上是我为演示该技术而编写的一个简单的 WPF 程序,但它可以毫无困难地适应 Winforms 或其他 GUI 框架。基本思想保持不变。