C# 在EnumerateFiles()LINQ C中处理(跳过)UnauthorizedAccessException
我正在尝试编写一个高性能的文件系统搜索程序,它可以搜索未索引的驱动器,包括本地驱动器和网络驱动器,并对扩展名和关键字进行快速过滤。我试图使用C的DirectoryInfo.EnumerateDirectory、DirectoryInfo.EnumerateFiles和LINQ查询来实现这一点。根据我的测试,这是迄今为止我能找到的性能最好的代码:C# 在EnumerateFiles()LINQ C中处理(跳过)UnauthorizedAccessException,c#,performance,linq,search,C#,Performance,Linq,Search,我正在尝试编写一个高性能的文件系统搜索程序,它可以搜索未索引的驱动器,包括本地驱动器和网络驱动器,并对扩展名和关键字进行快速过滤。我试图使用C的DirectoryInfo.EnumerateDirectory、DirectoryInfo.EnumerateFiles和LINQ查询来实现这一点。根据我的测试,这是迄今为止我能找到的性能最好的代码: FileInfo[] dirFiles = dirInfo.EnumerateDirectories()
FileInfo[] dirFiles = dirInfo.EnumerateDirectories()
.AsParallel()
.SelectMany(di => di.EnumerateFiles("*.*", SearchOption.AllDirectories)
.Where(fi => EndsWithExtension(fi.Extension)) )
.ToArray();
但是,UnauthorizedAccessException不会被处理,抛出时会使整个查询崩溃
我已经尝试过与此问题相关的各种方法,但我发现它们在搜索性能方面明显较慢。我发现第二好的方法工作起来要慢20多倍,例如:
try {
foreach (string fileName in EnumerateFiles(dirInfo, "*.*", SearchOption.AllDirectories)) {
if (ContainsKeyword(fileName)) {
Results.Add(fileName.FullName);
}
}
} catch (Exception e) { continue; }
当目录抛出异常时,我想跳过它。我一直在尝试用类似的东西来实现这一点,但我无法让它发挥作用我对LINQ和枚举的知识太有限了…:
FileInfo[] dirFiles = dirInfo.EnumerateDirectories()
.AsParallel()
.SelectMany(di => di.EnumerateFiles("*.*", SearchOption.AllDirectories)
.SkipExceptions()
.Where(fi => EndsWithExtension(fi.Extension)) )
.ToArray();
public static class Extensions {
public static IEnumerable<T> SkipExceptions<T>(this IEnumerable<T> values) {
using (var enumerator = values.GetEnumerator()) {
bool next = true;
while (next) {
try {
if (enumerator.Current != null)
Console.WriteLine(enumerator.Current.ToString());
next = enumerator.MoveNext();
} catch {
continue;
}
if (next) yield return enumerator.Current;
}
}
}
}
是否可以处理未经授权的数据访问异常,同时仍保持原始LINQ查询的高性能
提前感谢您的帮助 答案已编辑:
解决方法是递归调用它,而不是使用SearchOption.AllDirectories。在您的情况下,这实际上效率更低,因为您不需要将文件系统中的每个文件加载到一个数组中。从以下帮助器方法开始:
List<string> GetDirectoriesRecursive (string parent)
{
var directories = new List<string>();
GetDirectoriesRecursive (directories, parent);
return directories;
}
void GetDirectoriesRecursive (List<string> directories, string parent)
{
directories.Add (parent);
foreach (string child in GetAuthorizedDirectories (parent))
GetDirectoriesRecursive (directories, child);
}
string[] GetAuthorizedDirectories (string dir)
{
try { return Directory.GetDirectories (dir); }
catch (UnauthorizedAccessException) { return new string[0]; }
}
string[] GetAuthorizedFiles (string dir)
{
try { return Directory.GetFiles (dir); }
catch (UnauthorizedAccessException) { return new string[0]; }
}
或者,仅获取其目录:
var foldersWithBigFiles =
from dir in GetDirectoriesRecursive ( @"c:\" )
where GetAuthorizedFiles (dir).Any (f => new FileInfo (f).Length > 100000000 )
select dir;
另一种方法:
string[] directories = Directory.EnumerateDirectories(@"\\testnetwork\abc$","*.*", SearchOption.AllDirectories).Catch(typeof(UnauthorizedAccessException)).ToArray();
新增缺失部分:
static class ExceptionExtensions
{
public static IEnumerable<TIn> Catch<TIn>(
this IEnumerable<TIn> source,
Type exceptionType)
{
using (var e = source.GetEnumerator())
while (true)
{
var ok = false;
try
{
ok = e.MoveNext();
}
catch(Exception ex)
{
if (ex.GetType() != exceptionType)
throw;
continue;
}
if (!ok)
yield break;
yield return e.Current;
}
}
}
对于链接是否包含解决方案,这是一个描述不当的答案。你应该在回答中详细说明至少部分链接。在你编辑后更改了我的投票。谢谢你的回答。愉快的编码。谢谢你的回答,但这并不是我问题的解决方案。使用各种helper方法的第一个建议有很多开销,它会将filepath字符串转换为FileInfo对象,通过递归建立大量内存。这会大大降低查询速度。后者确实更优雅。我已经看到了这个解决方案,但是你忘记了在最重要的部分复制:ExceptionExtension,但是我无法让它工作。它仍然抛出UnauthorizedAccessException。
static class ExceptionExtensions
{
public static IEnumerable<TIn> Catch<TIn>(
this IEnumerable<TIn> source,
Type exceptionType)
{
using (var e = source.GetEnumerator())
while (true)
{
var ok = false;
try
{
ok = e.MoveNext();
}
catch(Exception ex)
{
if (ex.GetType() != exceptionType)
throw;
continue;
}
if (!ok)
yield break;
yield return e.Current;
}
}
}