我有一个测试程序,它应该遍历C:下的所有文件。当它到达“Documents and Settings”文件夹时它会死掉。我想忽略错误并继续循环其余文件。问题是该异常在<{1}} 中抛出,因此在foreach
周围放置try/catch
将导致循环退出。并且在foreach
之后放置try/catch
永远不会触发(因为异常被抛出 foreach
)。有没有办法忽略异常并继续处理循环?
以下是代码:
foreach
答案 0 :(得分:3)
问题在于IEnumerable<string>
懒惰,(有点像流)。 foreach
从files
接受一个接一个的字符串,因此只有当它请求问题目录时,枚举器才会崩溃。 (您可以通过调用ToList()
或类似的内容来确认:
//should now crash here instead
List<string> files = Directory.EnumerateFiles(@"C:\", "*.*",
SearchOption.AllDirectories).ToList();
我认为您需要找到一种方法将files
转换为枚举集合,方法是从files
手动获取每个项目,并对枚举器的实际读取执行try / catch。
编辑:chris和servy在评论中有很好的分数。在抛出异常之后,你可能根本不应该弄乱枚举器。尝试在文件查询中更加保守可能是最简单的解决方案。
答案 1 :(得分: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);
}
}
现在我们只需抓取root,遍历它,并使用获取不会引发异常的子目录的方法:
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>();
}
}
请注意,您可以稍微使用此特定部分。即使您无法获得其他子目录,您也可以在这里获得一些子目录,您可能需要调整错误处理等。获取文件版本基本相同,但只是{{{ 1}}而不是。
答案 2 :(得分:1)
您可能会收到UnauthorizedAccessException,因为某些文件是隐藏的或系统文件。因为您正在使用字符串(来自enumeratefiles)而不是fileinfos或directoryinfo对象,所以我重写了这个以适合您的设计:
首先添加这两个成员变量:
private static Dictionary<DirectoryInfo, List<string>> DirectoryTree = new Dictionary<DirectoryInfo, List<string>>();
private static List<string> logger = new List<string>();
然后放置此方法:
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);
}
}
}
然后在Main中你这样称呼它:
RecursiveSearch(@"c:\");
将填写您的DirectoryTree词典和列表记录器。
答案 3 :(得分:-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)
{
}
}
}