C#如何在不使用大量内存的情况下循环递归地遍历一组大型文件夹和文件

4
我希望能够将我所有的音乐文件进行索引,并存储到数据库中。我有一个递归调用的函数,从我的音乐驱动器根目录开始执行。
例如:
start > ReadFiles(C:\music\);

ReadFiles(path){
   foreach(file)
      save to index;

   foreach(directory)
      ReadFiles(directory);
}

这个程序可以正常运行,但是在运行过程中使用的内存量会不断增加,最终导致系统内存耗尽。

有没有更好的方法来完成这个任务,而不需要4GB的RAM?

祝好,Tys


4
请发布实际代码。你的方法基本上没有问题。 - John Kugelman
3
递归不受可用内存的限制,而是受堆栈大小的限制。因此,如果你的内存不足,那么这似乎意味着你保存了数据时间过长。 - Brian Rasmussen
我无法想象你有足够的音乐需要那么多的空间。你确定你没有在某个地方进入了无限循环或者发生了堆栈溢出吗? - Michael Todd
2
请解释“保存到索引”。它实际上是将文件中的所有字节存储在运行程序内部的磁盘上吗? - Brian
5个回答

10

Alxandr的基于队列的解决方案应该可以正常工作。

如果你在使用.NET 4.0,你还可以利用新的Directory.EnumerateFiles方法,它会懒惰枚举文件,而不会将它们全部加载到内存中:

void ReadFiles(string path)
{
    IEnumerable<string> files =
        Directory.EnumerateFiles(
            path,
            "*",
            SearchOption.AllDirectories); // search recursively

    foreach(string file in files)
        SaveToIndex(file);
}

不错,我不知道这个 :-) - Alxandr
2
这是一个很好的答案,但它仍然不能解决根本问题——如果您面临内存问题,那么一定有某些东西占用了太多的内存。 - configurator
1
@configurator,你可能是对的...但根据OP提供的信息,这是我能做到的最好的;) - Thomas Levesque

2

你有检查过除根目录外的每个目录中都会出现的...条目吗?

如果你没有跳过它们,那么你将会陷入无限循环。


1
它们既不出现在Directory.GetFiles也不出现在Directory.GetDirectories中。通常情况下,在.NET中工作时不应遇到此问题。 - configurator

1

你可以将其实现为队列。我认为(但我不确定)这将节省内存。至少它会释放你的堆栈。每当你找到一个文件夹时,就把它添加到队列中,每当你找到一个文件时,就读取它。这可以避免递归。

类似这样:

Queue<string> dirs = new Queue<string>();
dirs.Enqueue("basedir");
while(dirs.Count > 0) {
    foreach(directory)
        dirs.Enqueue(directory);
    ReadFiles();
}

1
这不会节省内存。默认情况下,堆栈在溢出前仅有1兆字节大小。如果他遇到了OutOfMemory问题,那么存在不同的问题。 - Brian
感谢所有的回答。每一个都有所帮助。我已经实现了一个队列机制,对不应该被索引的目录进行了一些额外的检查,并在此过程中发现我的NHibernate也需要一些微调。现在可以轻松地索引超过1TB的数据了。 - Tys

0

0

注意,如果您无法访问文件,路径太长或发生其他异常,EnumerateFiles() 将停止运行。目前我使用以下方法来解决这些问题:

public static List<string> getFiles(string path, List<string> files)
{
    IEnumerable<string> fileInfo = null;
    IEnumerable<string> folderInfo = null;
    try
    {
        fileInfo = Directory.EnumerateFiles(str);
    }
    catch
    {

    }
    if (fileInfo != null)
    {
        files.AddRange(fileInfo);
        //recurse through the subfolders
        fileInfo = Directory.EnumerateDirectories(str);
        foreach (string s in folderInfo)
        {
            try
            {
                getFiles(s, files);
            }
            catch
            {

            }
        }
    }
    return files;
}

使用示例:

List<string> files = new List<string>();
files = folder.getFiles(path, files);

我的解决方案基于此页面上的代码:http://msdn.microsoft.com/en-us/library/vstudio/bb513869.aspx

更新:可以在http://social.msdn.microsoft.com/Forums/vstudio/en-US/ae61e5a6-97f9-4eaa-9f1a-856541c6dcce/directorygetfiles-gives-me-access-denied?forum=csharpgeneral找到一个更快的递归获取文件的方法。使用Stack对我来说是新的(我甚至不知道它的存在),但这种方法似乎有效。至少它列出了我C和D分区上的所有文件,没有错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接