【问题标题】:What is the equivalent of EnumerateFiles() in Net 2.0?Net 2.0 中的 EnumerateFiles() 等价物是什么?
【发布时间】:2014-06-29 13:19:20
【问题描述】:

我正在处理大量文件,因此,我不想等到整个搜索完成后才返回数组。所以我不想使用 Directory.GetFiles()

根据this answer ,我需要使用 EnumerateFiles() 才能在搜索过程中获得结果。但是,我使用的是 NET2.0,这个功能似乎是从 NET 4.0 开始引入的

Net 2.0 中的 EnumerateFiles() 等价物是什么?

任何提示将不胜感激

【问题讨论】:

  • 没有等价物。但如果处理复杂且昂贵,Directory.GetFiles 是错误的优化位置。您可以优化处理方法,也可以使用GetFiles 加载所有路径,然后一个接一个地处理,例如每10个文件。
  • 你认为这个答案是我需要的stackoverflow.com/a/929418/2340370 吗?
  • 它仍然首先使用Directory.GetFiles(path),即使迭代器一个接一个地产生。所以不,这只是伪造具有大量文件的单个目录的延迟执行。
  • 一个目录下有那么多文件让GetFiles变得昂贵吗?还是只是因为您搜索子目录而昂贵?在后一种情况下,您可以简单地自己编写递归下降,并且您只会在开始搜索之前产生浅搜索的成本。一种实现是Enumerating Files Throwing Exception
  • 如果这些文件在同一个目录下,那么你可能需要看一下winapi方向:FindFirstFileFindNextFile等。

标签: c# .net-2.0


【解决方案1】:

您需要的是FindFirstFile and FindNextFile 的 WinAPI 调用。 下面是一些使用封装的 api 调用的代码。

IEnumerable<string> EnumerateFiles(string path)
{
    APIWrapper.FindData findData = new APIWrapper.FindData();

    APIWrapper.SafeFindHandle handle = APIWrapper.SafeNativeMethods.FindFirstFile(System.IO.Path.Combine(path, "*"), findData);
    if(!handle.IsInvalid && !handle.IsClosed)
    {
        yield return findData.fileName;

        while(!APIWrapper.SafeNativeMethods.FindNextFile(handle, findData))
            yield return findData.fileName;
        handle.Close();
    }
}

我只是手动输入了EnumerateFiles,所以将其视为伪代码,但它所依赖的类已准备好生产,就在这里

internal class APIWrapper
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    internal sealed class FILETIME
    {
        public int Low;
        public int High;
        public Int64 ToInt64()
        {
            Int64 h = High;

            h = h << 32;
            return h + Low;
        }
    }


    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    internal sealed class FindData
    {
        public int fileAttributes;
        public FILETIME CreationTime;
        public FILETIME LastAccessTime;
        public FILETIME LastWriteTime;
        public int FileSizeHigh;
        public int FileSizeLow;
        public int dwReserved0;
        public int dwReserved1;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public String fileName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
        public String alternateFileName;
    }
    internal sealed class SafeFindHandle : Microsoft.Win32.SafeHandles.SafeHandleMinusOneIsInvalid
    {
        /// <summary>
        /// Constructor
        /// </summary>
        public SafeFindHandle()
            : base(true)
        {
        }

        /// <summary>
        /// Release the find handle
        /// </summary>
        /// <returns>true if the handle was released</returns>
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        protected override bool ReleaseHandle()
        {
            return SafeNativeMethods.FindClose(handle);
        }
    }

    internal enum SearchOptions
    {
        NameMatch,
        LimitToDirectories,
        LimitToDevices
    }
    [SecurityPermissionAttribute(SecurityAction.Assert, UnmanagedCode = true)]
    internal static class SafeNativeMethods
    {
        [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
        public static extern SafeFindHandle FindFirstFile(String fileName, [In, Out] FindData findFileData);

        [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
        public static extern SafeFindHandle FindFirstFileEx(
            String fileName,                    //__in        LPCTSTR lpFileName,
            [In] int infoLevel,                 //__in        FINDEX_INFO_LEVELS fInfoLevelId,
            [In, Out] FindData findFileData,    //__out       LPVOID lpFindFileData,
            [In, Out] SearchOptions SerchOps,             //__in        FINDEX_SEARCH_OPS fSearchOp,
            [In] int SearchFilter,              //__reserved  LPVOID lpSearchFilter,
            [In] int AdditionalFlags);          //__in        DWORD dwAdditionalFlags

        [DllImport("kernel32", CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool FindNextFile(SafeFindHandle hFindFile, [In, Out] FindData lpFindFileData);

        [DllImport("kernel32", CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool FindClose(IntPtr hFindFile);
    }
}

【讨论】:

  • 不包含不包含文件的目录?你返回一个文件名而不检查FindFirstFile 是否成功,
  • 刚刚发现并修复了它。就像我说的,将 EnumerateFiles 视为伪代码。感谢您的反馈。
  • 没有解释的反对票,这怎么可能?伙计们,如果我的回答有问题,你能告诉我是什么吗?毕竟我们都是来学习的:)
  • 尝试用它读取system32,唯一返回的是.
【解决方案2】:

特别添加为新答案..

从.NET 2.0 开始就有 IENumerable 和 yield 关键字做延迟初始化/延迟执行..有了这些,你可以得到你想要的。

public IEnumerable<string> GetFiles(string rootPath, string [] fileNameStartChars, string[] extensionsFilter)
        {
            FileSystemInfo[] fsi = null;
            for(int i = 0; i < fileNameStartChars.Length; i++)
            {
                for(int k = 0; k<extensionsFilter.Length; k++)
                {
                    fsi = new DirectoryInfo(rootPath).GetFileSystemInfos(fileNameStartChars[i]+extensionsFilter[k]);

                    if (fsi.Length > 0)
                    {
                        for (int j = 0; j < fsi.Length; j++)
                        {

                          /// .Name returns the filename with extension..if you need, please implement here a substring for eliminate the extension of the file
                            yield return fsi[j].Name;
                        }
                    }
                }

            }

        }

及用法:

可能的文件名 startsWithChar 表

public string[] table = new string[]
        {
          "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
          "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
          "1","2","3","4","5","6","7","8","9","0","#","_","-",".","@","+",",","%","&","(",")","[","]","{","}","*",
          "<",">","^"," ","|",";","`"
         };

和扩展:

string[] Exts = new string[] { ".mp3", ".midi", ".wav"};

使用这种方法,您可以过滤小部分中的数据,例如使用startswithchar过滤,这样您就不会遇到取决于您的文件数量的内存问题。这是尝试模仿.net v4的棘手部分使用 100% .net v2 托管代码的 EnumerateFiles 方法..

     IEnumerable<string> strNumerable = GetFiles(@"D:\Music", table, Exts);


///Since its deferred execution, method didn't get any memory alloc for your data till now..Memory Alloc will start within this foreach..

            foreach (string s in strNumerable)
            {
               //do your work
            }

【讨论】:

    【解决方案3】:

    从 .NET 2.0 开始,有 IENumerableyield 关键字可以做到 Lazy Initialization.. 有了这些,你可以得到你想要的。

    使用伪:

    public IENumerable GetFiles(string Path, string FileExtension)
    {
       // Create a new IENumerable instance
       // Get FileCount with DirectoryInfo or some similar
       // Implement a for-loop with File count
       // If DirectoryFiles [ indexOfForLoop ] .Extension == FileExtension
    
       yield return DirectoryFiles [indexOfForLoop ]
    }
    

    在这个伪中yield关键字负责过滤。如果过滤返回true,yield return立即将结果返回给IENumerable实例/被调用者..

    IEnumerable 负责延迟加载..

    取决于您的需要,您也可以在循环中使用yield break 关键字来不包含结果..

    只需一个简单的调用:

    List<string> FilesInDirectory = GetFiles( path, "*.txt").ToList();
    

    希望这会有所帮助..

    【讨论】:

    • 。 . .你从哪里得到实际的文件名?
    • @BinaryWorrier 如果 Op 想要,在 for 循环中可以有一个 FileInfo..或者如 Op 想要的那样,在方法之外,在使用另一个循环/线程列出结果后,他可以获得文件名..正如我们所知,yield 关键字立即返回。所以,无需等待整个数组/列表执行;)
    • 不,抱歉,我听不懂,您能否添加一些实际代码来显示您从何处获取文件名?
    • 不需要关注?当然,任何阅读答案的人都应该能够遵循它。如果您可以用一些实际获取文件名的方法替换方便的波浪 cmets,我会深表歉意并支持您的答案。谢谢:)
    • 老兄 这么亲近的人生病了,我真的很抱歉。您似乎已经读过我的 cmets 要求以敌意和/或傲慢的态度进行指责,这与事实相去甚远。请不要把这些东西放在个人身上,我们都在这里学习,并希望通过留下好的问题和答案让互联网变得更美好。我希望 SO 上的每个答案都是最好的答案。再说一次,这不是个人攻击,我不认识你或关于你的任何事情。我只知道你回答的内容。
    猜你喜欢
    • 2010-10-02
    • 2013-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-05
    • 2011-10-13
    • 2011-03-23
    相关资源
    最近更新 更多