6

我有一个测试程序,它应该遍历 C: 下的所有文件。当它点击“文档和设置”文件夹时它会死掉。我想忽略该错误并继续遍历剩余的文件。问题是在 中抛出了异常,所以foreachtry/catch周围放一个foreach会导致循环退出。try/catch并在永远不会触发之后放置 a (因为在中引发foreach异常)。有什么方法可以忽略异常并继续处理循环?foreach

这是代码:

static void Main(string[] args)
{
    IEnumerable<string> files = Directory.EnumerateFiles(@"C:\", "*.*",
                                SearchOption.AllDirectories);
    foreach (string file in files)  // Exception occurs when evaluating "file"
        Console.WriteLine(file);
}
4

4 回答 4

3

您没有有效的方法来处理这种情况。如果迭代器本身在尝试获取下一个项目时抛出异常,那么它将无法在此之后为您提供该项目;它不再处于“有效”状态。一旦抛出异常,您就完成了该迭代器。

您唯一的选择是不使用此方法查询整个驱动器。也许您可以编写自己的迭代器来手动遍历驱动器,从而更优雅地跳过无法遍历的项目。

所以,要做这个手动遍历。我们可以从一个Traverse可以遍历任何树的泛化方法开始(非递归,以更好地有效处理深堆栈):

public static IEnumerable<T> Traverse<T>(
    this IEnumerable<T> source
    , Func<T, IEnumerable<T>> childrenSelector)
{
    var stack = new Stack<T>(source);
    while (stack.Any())
    {
        var next = stack.Pop();
        yield return next;
        foreach (var child in childrenSelector(next))
            stack.Push(child);
    }
}

现在我们只需获取根目录,遍历它,并使用一种获取不会抛出异常的子目录的方法:

var root = new DirectoryInfo("C:\\");
var allFiles = new[] { root }.Traverse(dir => GetDirectoriesWithoutThrowing(dir))
    .SelectMany(dir => GetFilesWithoutThrowing(dir));

获取子目录的方法可以像这样开始,但可能需要充实:

private static IEnumerable<DirectoryInfo> GetDirectoriesWithoutThrowing(
    DirectoryInfo dir)
{
    try
    {
        return dir.GetDirectories();
    }
    catch (Exception)//if possible catch a more derived exception
    {
        //TODO consider logging the exception
        return Enumerable.Empty<DirectoryInfo>();
    }
}

请注意,您可以稍微玩一下这个特定的部分。即使您无法获取其他子目录,您也可以在此处获取一些子目录,您可能需要调整错误处理等。获取文件版本将基本相同,但只是GetFiles替换。

于 2013-10-24T16:02:29.427 回答
3

问题是IEnumerable<string>行为懒惰,(有点像流)。foreach从 中获取一个接一个的字符串files,因此只有当它请求问题目录时,枚举器才会崩溃。(您可以通过致电ToList()或类似的方式确认这一点:

//should now crash here instead
List<string> files = Directory.EnumerateFiles(@"C:\", "*.*",
                            SearchOption.AllDirectories).ToList();

我认为您需要找到一种方法来转换files为枚举集合,方法是手动从枚举器中获取每个项目files并围绕枚举器的实际读取进行尝试/捕捉。

编辑:克里斯和服务在评论中有很好的观点。在枚举器引发异常之后,您可能根本不应该弄乱它。尝试在文件查询中更加保守可能是最简单的解决方案。

于 2013-10-24T15:53:04.967 回答
1

You are probably getting an UnauthorizedAccessException because some files are hidden or a system file.Since you are working with strings(from enumeratefiles)and not fileinfos or directoryinfo objects i rewrote this to fit your design:

first add these 2 member variables:

private static Dictionary<DirectoryInfo, List<string>> DirectoryTree = new Dictionary<DirectoryInfo, List<string>>();
private static List<string> logger = new List<string>();

then place this method:

static void RecursiveSearch(string root)
{
     string[] files = null;
     string[] subDirs = null;

     // First, process all the files directly under this folder 
     try
     {
        files = Directory.EnumerateFiles(root).ToArray();
     }
     catch (UnauthorizedAccessException e)
     {
         logger.Add(e.Message);
     }
     catch (System.IO.DirectoryNotFoundException e)
     {
         Console.WriteLine(e.Message);
     }

     if (files != null)
     {

          DirectoryTree.Add(new DirectoryInfo(root), files.ToList());
          subDirs = Directory.GetDirectories(root);

          foreach (string dir in subDirs)
          {
              RecursiveSearch(dir);
          }
     }
}

Then in Main you call it like so:

RecursiveSearch(@"c:\");

After your DirectoryTree dictionary and list logger will be filled.

于 2013-10-24T16:08:58.760 回答
-3
static void Main(string[] args)
{
    IEnumerable<string> files = Directory.EnumerateFiles(@"C:\", "*.*",
                            SearchOption.AllDirectories);
    foreach (string file in files)  // Exception occurs when evaluating "file"
    {
        try
        {
            Console.WriteLine(file);
        }
        Catch(Exception ex)
        {
        }
    }
}
于 2013-10-24T15:50:23.683 回答