【问题标题】:Fastest/safest file finding/parsing?最快/最安全的文件查找/解析?
【发布时间】:2012-04-02 13:02:32
【问题描述】:

c:,我有数以万计的*.foobar 文件。它们在各种地方(即子目录)。这些文件大小约为 1 - 64 kb,为纯文本。

我有一个 class Foobar(string fileContents) 可以强类型化这些 .foobar 文件。

我的挑战是获取c: 上所有*.foobar 文件的列表,表示为Foobar 对象数组。最快的方法是什么?

我很想知道是否有比我的第一种方法更好的方法(毫无疑问),我的这种方法是否有任何潜在问题(例如 I/O 并发问题引发异常?):

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();

【问题讨论】:

  • 并行执行操作可能不会给您带来太多好处;在物理磁盘上搜索文件必然是 I/O 绑定操作。
  • 愚蠢的问题:文件搜索真的需要磁盘 I/O 吗?我认为磁盘的文件系统结构由操作系统内核缓存在内存中,并且仅根据需要进行更新,因为该结构与磁盘上的内容是分开的。没有?
  • 如果搜索受 I/O 限制,.Parallel() 唯一需要购买的是线程化 new Foobar() 操作(这可能需要时间;毕竟它必须解析一个巨大的字符串) .正确的?我想知道为每个 new Foobar() 启动新线程的成本是否比在单个线程中连续创建 new Foobar() 对象更昂贵。
  • 你到底想做什么?如果您尝试搜索文件内容,请考虑使用 Windows Indexing Service 或 dtSearch 等索引服务。
  • 无论你做什么都会很慢。您当前方法的最大问题是它非常不可靠。锁定这数千个文件中的 一个,运行此代码一分钟后,您将看到一个异常。

标签: c# .net file parsing search


【解决方案1】:

干得好,这是您代码的扩展,可返回 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();
    }
}

【讨论】:

    【解决方案2】:

    因为权限错误(或其他错误)显然会使枚举停止运行,您可能希望实现自己的枚举器,如下所示:

    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();
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-27
      相关资源
      最近更新 更多