1

我需要在所有连接的驱动器(逻辑驱动器和物理驱动器)中搜索特定文件类型(例如 mp4)。我知道我可以编写一个递归函数来做到这一点。但我正在寻找一种最有效的方法,因为这可能是一个耗时且消耗 CPU 的操作。

4

2 回答 2

0

你可以利用这个dir命令,让文件系统做它擅长的事情。

static string[] SearchFiles(params string[] patterns)
{
    var searchPatterns = DriveInfo.GetDrives()
        .Where(d => d.IsReady && d.DriveType != DriveType.NoRootDirectory)
        .SelectMany(d => patterns.Select(p => d.RootDirectory + p));

    using (var process = new Process())
    {
        process.StartInfo.FileName = Path.Combine(Environment.SystemDirectory, "cmd.exe");
        process.StartInfo.Arguments = "/C dir " + String.Join(" ", searchPatterns) + " /s/b";
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.CreateNoWindow = true;
        process.Start();

        string strOutput = process.StandardOutput.ReadToEnd();
        process.WaitForExit();

        return strOutput.Split(Environment.NewLine.ToArray());
    }
}

并像这样使用它:

var files = SearchFiles("*.jpg", "*.mp?", "*.mpeg");

由于此操作可能需要一些时间,您可以使用BackgroundWorker在后台线程中运行它。

此外,由于输出可能非常大,您可能会考虑输出到文件(例如,通过添加> out.txtafter /s/b)并处理文件,而不是返回字符串数组。

编辑:
您可以通过并行搜索驱动器来提高性能。

static List<string> SearchFiles(params string[] patterns)
{
    var result = new List<string>();

    var drives = DriveInfo.GetDrives();
    Parallel.ForEach(drives, drive =>
    {
        if (!drive.IsReady || drive.DriveType == DriveType.NoRootDirectory)
            return;

        var searchPatterns = patterns.Select(p => drive.RootDirectory + p);

        using (var process = new Process())
        {
            process.StartInfo.FileName = Path.Combine(Environment.SystemDirectory, "cmd.exe");
            process.StartInfo.Arguments = "/C dir " + String.Join(" ", searchPatterns) + " /s/b";
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.CreateNoWindow = true;
            process.Start();

            string strOutput = process.StandardOutput.ReadToEnd();
            process.WaitForExit();

            result.AddRange(strOutput.Split(Environment.NewLine.ToArray(), StringSplitOptions.RemoveEmptyEntries));
        }
    });

    return result;
}
于 2012-05-26T13:12:18.440 回答
0

最后我让它工作了。代码如下:

 static List<string> SearchFiles(string pattern)
    {
        var result = new List<string>();

        foreach (string drive in Directory.GetLogicalDrives())
        {
            Console.WriteLine("searching " + drive);
            var files = FindAccessableFiles(drive, pattern, true);
            Console.WriteLine(files.Count().ToString() + " files found.");
            result.AddRange(files);
        }

        return result;
    }

    private static IEnumerable<String> FindAccessableFiles(string path, string file_pattern, bool recurse)
    {
        Console.WriteLine(path);
        var list = new List<string>();
        var required_extension = "mp4";

        if (File.Exists(path))
        {
            yield return path;
            yield break;
        }

        if (!Directory.Exists(path))
        {
            yield break;
        }

        if (null == file_pattern)
            file_pattern = "*." + required_extension;

        var top_directory = new DirectoryInfo(path);

        // Enumerate the files just in the top directory.
        IEnumerator<FileInfo> files;
        try
        {
            files = top_directory.EnumerateFiles(file_pattern).GetEnumerator();
        }
        catch (Exception ex)
        {
            files = null;
        }

        while (true)
        {
            FileInfo file = null;
            try
            {
                if (files != null && files.MoveNext())
                    file = files.Current;
                else
                    break;
            }
            catch (UnauthorizedAccessException)
            {
                continue;
            }
            catch (PathTooLongException)
            {
                continue;
            }

            yield return file.FullName;
        }

        if (!recurse)
            yield break;

        IEnumerator<DirectoryInfo> dirs;
        try
        {
            dirs = top_directory.EnumerateDirectories("*").GetEnumerator();
        }
        catch (Exception ex)
        {
            dirs = null;
        }


        while (true)
        {
            DirectoryInfo dir = null;
            try
            {
                if (dirs != null && dirs.MoveNext())
                    dir = dirs.Current;
                else
                    break;
            }
            catch (UnauthorizedAccessException)
            {
                continue;
            }
            catch (PathTooLongException)
            {
                continue;
            }

            foreach (var subpath in FindAccessableFiles(dir.FullName, file_pattern, recurse))
                yield return subpath;
        }
    }
于 2012-05-26T15:22:26.000 回答