2012-03-17 2 views
5

c:에 수만개의 파일이 있습니다. *.foobar 개의 파일이 있습니다. 그들은 모든 종류의 장소 (예 : 하위 디렉토리)에 있습니다. 이 파일의 크기는 약 1 - 64kb이며 일반 텍스트입니다.가장 빠르고 안전한 파일 찾기/파싱?

.foobar 파일을 강력하게 입력하는 class Foobar(string fileContents)이 있습니다.

나의 도전 과제는 c:에있는 *.foobar 파일의 목록을 Foobar의 배열로 표시하는 것입니다. 이 작업을 수행하는 가장 빠른 방법은 무엇입니까?

내 첫 번째 접근 방식보다 더 나은 방법이 있는지 (예 : I/O 동시성 문제가 예외를 throw하는 등) 어떤 문제가 있는지 알고 싶습니다.

var files = Directory.EnumerateFiles 
       (rootPath, "*.foobar", SearchOption.AllDirectories); 

Foobar[] foobars = 
(
    from filePath in files.AsParallel() 
    let contents = File.ReadAllText(filePath) 
    select new Foobar(contents) 
) 
.ToArray(); 
+8

아마도 병렬 작업을하는 것이 너를 많이 사지는 않을 것이다. 물리 디스크에서 파일을 검색하는 것은 필연적으로 I/O 바운드 작업입니다. –

+0

바보 같은 질문 : 파일 검색이 실제로 필요한 디스크 I/O를 수행합니까? 필자는 디스크의 파일 시스템 구조가 운영 체제 커널에 의해 메모리에 캐싱되고 구조가 디스크의 내용과 분리되어 있기 때문에 필요할 때만 업데이트한다고 생각합니다. 아니? – user979672

+0

검색이 I/O 바인딩 인 경우,'.Parallel()'만 사는 것은'new Foobar()'연산을 스레드하는 것입니다 (시간이 걸릴 수 있으며, 결국 거대한 문자열을 구문 분석해야합니다). 옳은? 'new Foobar()'마다 새로운 쓰레드를 만드는 데 드는 비용이 단일 쓰레드에서 연속적으로'new Foobar()'객체를 생성하는 것보다 비용이 많이 드는 지 궁금합니다. – user979672

답변

8

권한 오류 (또는 다른 오류가) 분명히 해당 트랙에 열거 죽은 중지 할 수 있기 때문에,이 같은 자신의 열거 뭔가 구현할 수 있습니다 :

class SafeFileEnumerator : IEnumerable<string> 
{ 
    private string root; 
    private string pattern; 
    private IList<Exception> errors; 
    public SafeFileEnumerator(string root, string pattern) 
    { 
    this.root = root; 
    this.pattern = pattern; 
    this.errors = new List<Exception>(); 
    } 

    public SafeFileEnumerator(string root, string pattern, IList<Exception> errors) 
    { 
    this.root = root; 
    this.pattern = pattern; 
    this.errors = errors; 
    } 

    public Exception[] Errors() 
    { 
    return errors.ToArray(); 
    } 
    class Enumerator : IEnumerator<string> 
    { 
    IEnumerator<string> fileEnumerator; 
    IEnumerator<string> directoryEnumerator; 
    string root; 
    string pattern; 
    IList<Exception> errors; 

    public Enumerator(string root, string pattern, IList<Exception> errors) 
    { 
     this.root = root; 
     this.pattern = pattern; 
     this.errors = errors; 
     fileEnumerator = System.IO.Directory.EnumerateFiles(root, pattern).GetEnumerator(); 
     directoryEnumerator = System.IO.Directory.EnumerateDirectories(root).GetEnumerator(); 
    } 
    public string Current 
    { 
     get 
     { 
      if (fileEnumerator == null) throw new ObjectDisposedException("FileEnumerator"); 
      return fileEnumerator.Current; 
     } 
    } 

    public void Dispose() 
    { 
     if (fileEnumerator != null) 
      fileEnumerator.Dispose(); 
     fileEnumerator = null; 
     if (directoryEnumerator != null) 
      directoryEnumerator.Dispose(); 
     directoryEnumerator = null; 
    } 

    object System.Collections.IEnumerator.Current 
    { 
     get { return Current; } 
    } 

    public bool MoveNext() 
    { 
     if ((fileEnumerator != null) && (fileEnumerator.MoveNext())) 
      return true; 
     while ((directoryEnumerator != null) && (directoryEnumerator.MoveNext())) 
     { 
      if (fileEnumerator != null) 
       fileEnumerator.Dispose(); 
      try 
      { 
       fileEnumerator = new SafeFileEnumerator(directoryEnumerator.Current, pattern, errors).GetEnumerator(); 
      } 
      catch (Exception ex) 
      { 
       errors.Add(ex); 
       continue; 
      } 
      if (fileEnumerator.MoveNext()) 
       return true; 
     } 
     if (fileEnumerator != null) 
      fileEnumerator.Dispose(); 
     fileEnumerator = null; 
     if (directoryEnumerator != null) 
      directoryEnumerator.Dispose(); 
     directoryEnumerator = null; 
     return false; 
    } 

    public void Reset() 
    { 
     Dispose(); 
     fileEnumerator = System.IO.Directory.EnumerateFiles(root, pattern).GetEnumerator(); 
     directoryEnumerator = System.IO.Directory.EnumerateDirectories(root).GetEnumerator(); 
    } 
    } 
    public IEnumerator<string> GetEnumerator() 
    { 
    return new Enumerator(root, pattern, errors); 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
    return GetEnumerator(); 
    } 
} 
4

위대한 작품은 여기에 확장 FileSystemInfo를 반환하는 코드 문자열 경로 대신에. SearchOption (네이티브 .net과 같은) 추가와 같은 몇 가지 사소한 변경 사항과 초기 디렉토리의 오류 트래핑은 루트 폴더가 액세스가 거부 된 경우를 대비합니다. 원래 게시에 다시 한 번 감사드립니다!

public class SafeFileEnumerator : IEnumerable<FileSystemInfo> 
{ 
    /// <summary> 
    /// Starting directory to search from 
    /// </summary> 
    private DirectoryInfo root; 

    /// <summary> 
    /// Filter pattern 
    /// </summary> 
    private string pattern; 

    /// <summary> 
    /// Indicator if search is recursive or not 
    /// </summary> 
    private SearchOption searchOption; 

    /// <summary> 
    /// Any errors captured 
    /// </summary> 
    private IList<Exception> errors; 

    /// <summary> 
    /// Create an Enumerator that will scan the file system, skipping directories where access is denied 
    /// </summary> 
    /// <param name="root">Starting Directory</param> 
    /// <param name="pattern">Filter pattern</param> 
    /// <param name="option">Recursive or not</param> 
    public SafeFileEnumerator(string root, string pattern, SearchOption option) 
     : this(new DirectoryInfo(root), pattern, option) 
    {} 

    /// <summary> 
    /// Create an Enumerator that will scan the file system, skipping directories where access is denied 
    /// </summary> 
    /// <param name="root">Starting Directory</param> 
    /// <param name="pattern">Filter pattern</param> 
    /// <param name="option">Recursive or not</param> 
    public SafeFileEnumerator(DirectoryInfo root, string pattern, SearchOption option) 
     : this(root, pattern, option, new List<Exception>()) 
    {} 

    // Internal constructor for recursive itterator 
    private SafeFileEnumerator(DirectoryInfo root, string pattern, SearchOption option, IList<Exception> errors) 
    { 
     if (root == null || !root.Exists) 
     { 
      throw new ArgumentException("Root directory is not set or does not exist.", "root"); 
     } 
     this.root = root; 
     this.searchOption = option; 
     this.pattern = String.IsNullOrEmpty(pattern) 
      ? "*" 
      : pattern; 
     this.errors = errors; 
    } 

    /// <summary> 
    /// Errors captured while parsing the file system. 
    /// </summary> 
    public Exception[] Errors 
    { 
     get 
     { 
      return errors.ToArray(); 
     } 
    } 

    /// <summary> 
    /// Helper class to enumerate the file system. 
    /// </summary> 
    private class Enumerator : IEnumerator<FileSystemInfo> 
    { 
     // Core enumerator that we will be walking though 
     private IEnumerator<FileSystemInfo> fileEnumerator; 
     // Directory enumerator to capture access errors 
     private IEnumerator<DirectoryInfo> directoryEnumerator; 

     private DirectoryInfo root; 
     private string pattern; 
     private SearchOption searchOption; 
     private IList<Exception> errors; 

     public Enumerator(DirectoryInfo root, string pattern, SearchOption option, IList<Exception> errors) 
     { 
      this.root = root; 
      this.pattern = pattern; 
      this.errors = errors; 
      this.searchOption = option; 

      Reset(); 
     } 

     /// <summary> 
     /// Current item the primary itterator is pointing to 
     /// </summary> 
     public FileSystemInfo Current 
     { 
      get 
      { 
       //if (fileEnumerator == null) throw new ObjectDisposedException("FileEnumerator"); 
       return fileEnumerator.Current as FileSystemInfo; 
      } 
     } 

     object System.Collections.IEnumerator.Current 
     { 
      get { return Current; } 
     } 

     public void Dispose() 
     { 
      Dispose(true, true); 
     } 

     private void Dispose(bool file, bool dir) 
     { 
      if (file) 
      { 
       if (fileEnumerator != null) 
        fileEnumerator.Dispose(); 

       fileEnumerator = null; 
      } 

      if (dir) 
      { 
       if (directoryEnumerator != null) 
        directoryEnumerator.Dispose(); 

       directoryEnumerator = null; 
      } 
     } 

     public bool MoveNext() 
     { 
      // Enumerate the files in the current folder 
      if ((fileEnumerator != null) && (fileEnumerator.MoveNext())) 
       return true; 

      // Don't go recursive... 
      if (searchOption == SearchOption.TopDirectoryOnly) { return false; } 

      while ((directoryEnumerator != null) && (directoryEnumerator.MoveNext())) 
      { 
       Dispose(true, false); 

       try 
       { 
        fileEnumerator = new SafeFileEnumerator(
         directoryEnumerator.Current, 
         pattern, 
         SearchOption.AllDirectories, 
         errors 
         ).GetEnumerator(); 
       } 
       catch (Exception ex) 
       { 
        errors.Add(ex); 
        continue; 
       } 

       // Open up the current folder file enumerator 
       if (fileEnumerator.MoveNext()) 
        return true; 
      } 

      Dispose(true, true); 

      return false; 
     } 

     public void Reset() 
     { 
      Dispose(true,true); 

      // Safely get the enumerators, including in the case where the root is not accessable 
      if (root != null) 
      { 
       try 
       { 
        fileEnumerator = root.GetFileSystemInfos(pattern, SearchOption.TopDirectoryOnly).AsEnumerable<FileSystemInfo>().GetEnumerator(); 
       } 
       catch (Exception ex) 
       { 
        errors.Add(ex); 
        fileEnumerator = null; 
       } 

       try 
       { 
        directoryEnumerator = root.GetDirectories(pattern, SearchOption.TopDirectoryOnly).AsEnumerable<DirectoryInfo>().GetEnumerator(); 
       } 
       catch (Exception ex) 
       { 
        errors.Add(ex); 
        directoryEnumerator = null; 
       } 
      } 
     } 
    } 
    public IEnumerator<FileSystemInfo> GetEnumerator() 
    { 
     return new Enumerator(root, pattern, searchOption, errors); 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 
관련 문제