衡量递归搜索的进度 [英] Measure progress in recursive search

查看:67
本文介绍了衡量递归搜索的进度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个递归搜索,它在整个目录中搜索具有指定名称的文件.这需要相当长的时间,我想显示一个进度条,它至少显示一些进度(我不在乎进度是通过文件或目录的数量还是它们的实际大小来衡量的).

以下(递归代码)执行搜索:

private void searchFolder(PortableDeviceFolder parent, ref List result, string filename){foreach(parent.Files 中的 var 项目){如果(项目是 PortableDeviceFolder){searchFolder((PortableDeviceFolder)item, ref 结果, 文件名);}否则 if ((String.Compare(item.Name, filename)==0)){结果.添加(父);Console.WriteLine("在以下位置找到文件:" + parent.Name);}}}

我不知道如何获得进度.我正在考虑一种简单的算法,它可以为每个文件和文件夹测量相同的百分比;

  • 文件 (10%)
    • 私人(10%/3)
    • 工作(10%/3)
      • 项目 1 (10%/3/2)
      • project2 (10%/3/2)
    • 家庭 (10%/3)
  • ...

等等.

不幸的是,我无法在递归搜索中实现这一点.有人有时间给我举个例子吗?提前致谢

解决方案

评论员 TaW 的建议是,您可以将目录数作为进步的基础.但是有一个小问题,不是建议本身,而是代码的实现方式.

从您的问题中不清楚 PortableDeviceFolder 是什么.但它似乎抽象了文件和文件夹之间的区别,从 Files 属性返回两者.鉴于处理中最耗时的方面可能是实际检索给定目录的文件名,那么如果 PortableDeviceFolder 必须返回给定目录中的目录的唯一机制是 Files 属性,您必须最终枚举所有文件以及目录,忽略为生成计数而返回的文件.

换句话说,获取计数几乎与实际搜索给定名称所需的时间一样长.

因此,就本回答而言,我将假设 PortableDeviceFolder 类具有另一个属性 Folders,它返回目录本身.有了这样的属性,我们就可以利用 TaW 提供的建议.

首先,您需要获得该计数.这样做的方法如下所示:

private int CountFolders(PortableDeviceFolder rootFolder){返回 rootFolder.Folders.Select(folder => CountFolders(folder)).Sum() + 1;}

在显示任何进度之前会有一点延迟,因为当然没有有用的方法来预测上述过程需要多长时间.但它应该相对简短,因为我们只查看目录而不是所有文件.

为了能够在处理过程中更新进度,我们应该在单独的线程中运行搜索并使用 Dispatcher.Invoke() 来更新 ProgressBar.Value属性.搜索方法如下所示:

private void SearchFolder(PortableDeviceFolder parent,列表结果,字符串文件名,IProgress进步){foreach(parent.Files 中的 var 项目){PortableDeviceFolder 文件夹 = 项目为 PortableDeviceFolder;如果(文件夹!= null){SearchFolder(文件夹,结果,文件名,进度);}else if (item.Name.Equals(fileName, StringComparison.OrdinalIgnoreCase)){结果.添加(父);}}进度.报告(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;列表folders = new List();PortableDeviceFolder rootFolder = new WindowsDirectoryFolder(searchPath);progressBar1.Value = 0;progressBar1.Maximum = await Task.Run(() => CountFolders(rootFolder));进展进步 =new Progress(increment =>progressBar1.Value += increment);await Task.Run(() => SearchFolder(rootFolder, folders, searchText, progress));listBox1.ItemsSource = 文件夹;button.IsEnabled = true;}

请注意,这里我们异步执行 CountFolders()SearchFolder() 方法,以确保 UI 在工作完成时保持响应.>

以上来自我编写的一个简单的 WPF 程序来演示该技术,但它可以毫无困难地适用于 Winforms 或其他 GUI 框架.基本思想保持不变.

I have a recursive search, which searches for files with a specified name in a whole directory. This needs quite some time and I want to show a progressbar, which shows atleast some progress (I don't care if the progress is just measured by the number of files or directory or the actual size of them).

Following (recursive code) performs the 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);
        }    
    }
}

I couldn't figure out how to get the progress. I was thinking about an easy algorithm which measures the same percentage for each file and folder;

  • Documents (10%)
    • privat(10%/3)
    • work(10%/3)
      • project1 (10%/3/2)
      • project2 (10%/3/2)
    • familly (10%/3)
  • ...

and so on.

Unfortunately I wasn't able to implement this in the recursive search. Has anybody the time to give me an example? Thanks in advance

解决方案

Commenter TaW's suggestion that you can count the directories to use as the basis for progress is good. But there's a small problem, not with the suggestion itself but in the way your code seems to be implemented.

It's not clear from your question what PortableDeviceFolder is. But it appears to abstract the difference between a file and a folder, returning both from the Files property. Given that the most time-consuming aspect of your processing is likely the actual retrieval of the file names for a given directory, then if the only mechanism PortableDeviceFolder has to return the directories in a given directory is that Files property, you'd have to wind up enumerating all the files along with the directories, ignoring the files returned for the purpose of generating the count.

In other words, getting the count will take almost as long as the actual search for the given name.

So, for the purpose of this answer I will assume that the PortableDeviceFolder class has another property Folders which returns just the directories themselves. With such a property, then we can take advantage of the suggestion TaW's offered.

First, you'll need to get that count. A method to do that would look like this:

private int CountFolders(PortableDeviceFolder rootFolder)
{
    return rootFolder.Folders.Select(folder => CountFolders(folder)).Sum() + 1;
}

There will be a slight delay before any progress is shown, because of course there's no useful way to predict how long the above will take. But it should be relatively brief, since we are looking only at the directories and not all the files.

To be able to update the progress while the processing is going, we should run the search in a separate thread and use Dispatcher.Invoke() to update the ProgressBar.Value property. The search method then looks like this:

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

NOTE: it's not clear to me why you were passing the result parameter by-reference. That doesn't seem to be needed, and so I've change it to a regular by-value parameter.

In your UI code, you might call it like this:

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

Note that here we execute both the CountFolders() and the SearchFolder() method asynchronously, to ensure the UI remains responsive while the work is being done.

The above is from a simple WPF program I wrote to demonstrate the technique, but it can be adapted to Winforms or other GUI frameworks without difficulty. The basic idea remains the same.

这篇关于衡量递归搜索的进度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆