我们有时需要遍历某个目录下的文件和子目录,可以使用System.IO.DirectoryInfo.GetDirectories或 GetFiles来获得目录下的所有的文件和子目录,当这个目录下的内容比较多时,这个操作就比较耗时间,有时我们仅仅需要知道某个目录下是否有子目录, 这样的操作显然是浪费时间的。此时我们很容易想到三个Win32API函数 FindFirstFile,FindNextFile和FindClose。这三个API搭配使用就能遍历文件和子目录了,而且可以遍历的时候随时中 止,避免无谓的操作。

C#中可以使用foreach来遍历某个序列,遍历使用的对象必须实现 System.Collections.IEnumeable接口,而内部调用的遍历器则必须实现 System.Collections.IEnumerator , 为了使用方便,我们在使用FindFirstFile等API函数时封装为 IEnumerator,而且实际上是有条件封装的。

这里很多人就会提到C#调用API的执行效率问题,认为应当用C,C++调用API才是正道,使用C#调用则有些鸡肋。但在我个人编程经历中,也有 不少调用API的,经验发现其实效率问题不大,可以省略。我只是做常规的运行在PC机上面的软件,CPU通常超过1GHZ,而且无需考虑高实时性和高效 率。若过于考虑效率问题会加大软件开发消耗。从工程开发管理方面看是不合理的。我应当解决比较突出的效率问题,不突出的影响不大的效率问题有时间才去解 决。使用C#封装Win32API必然会降低执行效率,但是封装后使用方便快捷,综合考虑认为这是正确的。

这里说一下“技术镀金”这个问题,所谓技术镀金就是开发人员在项目软件开发中过于追求技术的完美性,试图在技术上镀上一层完美的金壳,导致软件开发 工作量加大,项目时间拉长,有可能导致项目的失败。我吃过“技术镀金”的苦头,现在我内心是追求完美的,但实际开发时经常有意压制追求完美的心思。

现在继续探讨封装大计,本次封装重点在于实现IEnumerator,而IEnumeable只是IEnumerator的一个包装。 IEnumerator实现方法 Reset , MoveNext 和属性 Current,Reset方法用于重新设置遍历器,MoveNext用于查找下一个文件或目录,而Current返回当前文件或目录。

这个遍历器还得注意FindClose的调用,必须在遍历完毕没有找到文件或子目录后调用,若不调用该API函数则会造成内存泄漏。

根据上述设计,我写出如下代码,这段代码功能单一,希望有人能用得上

/// <summary>
/// 文件或目录遍历器,本类型为 FileDirectoryEnumerator 的一个包装
/// </summary>
/// <remarks>
/// 
/// 编写 袁永福 ( http://www.xdesigner.cn )2006-12-8
/// 
/// 以下代码演示使用这个文件目录遍历器
/// 
/// FileDirectoryEnumerable e = new FileDirectoryEnumerable();
/// e.SearchPath = @"c:"";
/// e.ReturnStringType = true ;
/// e.SearchPattern = "*.exe";
/// e.SearchDirectory = false ;
/// e.SearchFile = true;
/// foreach (object name in e)
/// {
///     System.Console.WriteLine(name);
/// }
/// System.Console.ReadLine();
/// 
///</remarks>
public class FileDirectoryEnumerable : System.Collections.IEnumerable
{
    private bool bolReturnStringType = true;
    /// <summary>
    /// 是否以字符串方式返回查询结果,若返回true则当前对象返回为字符串,
    /// 否则返回 System.IO.FileInfo或System.IO.DirectoryInfo类型
    /// </summary>
    public bool ReturnStringType
    {
        get { return bolReturnStringType; }
        set { bolReturnStringType = value; }
    }

private string strSearchPattern = "*";
    /// <summary>
    /// 文件或目录名的通配符
    /// </summary>
    public string SearchPattern
    {
        get { return strSearchPattern; }
        set { strSearchPattern = value; }
    }
    private string strSearchPath = null;
    /// <summary>
    /// 搜索路径,必须为绝对路径
    /// </summary>
    public string SearchPath
    {
        get { return strSearchPath; }
        set { strSearchPath = value; }
    }

private bool bolSearchForFile = true;
    /// <summary>
    /// 是否查找文件
    /// </summary>
    public bool SearchForFile
    {
        get { return bolSearchForFile; }
        set { bolSearchForFile = value; }
    }
    private bool bolSearchForDirectory = true;
    /// <summary>
    /// 是否查找子目录
    /// </summary>
    public bool SearchForDirectory
    {
        get { return bolSearchForDirectory; }
        set { bolSearchForDirectory = value; }
    }

private bool bolThrowIOException = true;
    /// <summary>
    /// 发生IO错误时是否抛出异常
    /// </summary>
    public bool ThrowIOException
    {
        get { return this.bolThrowIOException; }
        set { this.bolThrowIOException = value; }
    }
    /// <summary>
    /// 返回内置的文件和目录遍历器
    /// </summary>
    /// <returns>遍历器对象</returns>
    public System.Collections.IEnumerator GetEnumerator()
    {
        FileDirectoryEnumerator e = new FileDirectoryEnumerator();
        e.ReturnStringType = this.bolReturnStringType;
        e.SearchForDirectory = this.bolSearchForDirectory;
        e.SearchForFile = this.bolSearchForFile;
        e.SearchPath = this.strSearchPath;
        e.SearchPattern = this.strSearchPattern;
        e.ThrowIOException = this.bolThrowIOException;
        myList.Add(e);
        return e;
    }
    /// <summary>
    /// 关闭对象
    /// </summary>
    public void Close()
    {
        foreach (FileDirectoryEnumerator e in myList)
        {
            e.Close();
        }
        myList.Clear();
    }

private System.Collections.ArrayList myList = new System.Collections.ArrayList();

}//public class FileDirectoryEnumerable : System.Collections.IEnumerable

/// <summary>
/// 文件和目录的遍历器
/// </summary>
/// <remarks>本对象为Win32API函数 FindFirstFile , FindNextFile 
/// 和 FindClose 的一个包装
/// 
/// 以下代码演示使用了 FileDirectoryEnumerator 
/// 
/// FileDirectoryEnumerator e = new FileDirectoryEnumerator();
/// e.SearchPath = @"c:"";
/// e.Reset();
/// e.ReturnStringType = true ;
/// while (e.MoveNext())
/// {
///     System.Console.WriteLine
///         ( e.LastAccessTime.ToString("yyyy-MM-dd HH:mm:ss")
///         + "   " + e.FileLength + "  "t" + e.Name );
/// }
/// e.Close();
/// System.Console.ReadLine();
/// 
/// 编写 袁永福 ( http://www.xdesigner.cn )2006-12-8</remarks>
public class FileDirectoryEnumerator : System.Collections.IEnumerator
{
    
    #region 表示对象当前状态的数据和属性 **********************************

/// <summary>
    /// 当前对象
    /// </summary>
    private object objCurrentObject = null;

private bool bolIsEmpty = false;
    /// <summary>
    /// 该目录为空
    /// </summary>
    public bool IsEmpty
    {
        get { return bolIsEmpty; }
    }
    private int intSearchedCount = 0;
    /// <summary>
    /// 已找到的对象的个数
    /// </summary>
    public int SearchedCount
    {
        get { return intSearchedCount; }
    }
    private bool bolIsFile = true;
    /// <summary>
    /// 当前对象是否为文件,若为true则当前对象为文件,否则为目录
    /// </summary>
    public bool IsFile
    {
        get { return bolIsFile; }
    }
    private int intLastErrorCode = 0;
    /// <summary>
    /// 最后一次操作的Win32错误代码
    /// </summary>
    public int LastErrorCode
    {
        get { return intLastErrorCode; }
    }
    /// <summary>
    /// 当前对象的名称
    /// </summary>
    public string Name
    {
        get
        {
            if (this.objCurrentObject != null)
            {
                if (objCurrentObject is string)
                    return (string)this.objCurrentObject;
                else
                    return ((System.IO.FileSystemInfo)this.objCurrentObject).Name;
            }
            return null;
        }
    }
    /// <summary>
    /// 当前对象属性
    /// </summary>
    public System.IO.FileAttributes Attributes
    {
        get { return (System.IO.FileAttributes)myData.dwFileAttributes; }
    }
    /// <summary>
    /// 当前对象创建时间
    /// </summary>
    public System.DateTime CreationTime
    {
        get
        {
            long time = ToLong(myData.ftCreationTime_dwHighDateTime, myData.ftCreationTime_dwLowDateTime);
            System.DateTime dtm = System.DateTime.FromFileTimeUtc(time);
            return dtm.ToLocalTime();
        }
    }
    /// <summary>
    /// 当前对象最后访问时间
    /// </summary>
    public System.DateTime LastAccessTime
    {
        get
        {
            long time = ToLong(myData.ftLastAccessTime_dwHighDateTime, myData.ftLastAccessTime_dwLowDateTime);
            System.DateTime dtm = System.DateTime.FromFileTimeUtc(time);
            return dtm.ToLocalTime();
        }
    }
    /// <summary>
    /// 当前对象最后保存时间
    /// </summary>
    public System.DateTime LastWriteTime
    {
        get
        {
            long time = ToLong(myData.ftLastWriteTime_dwHighDateTime, myData.ftLastWriteTime_dwLowDateTime);
            System.DateTime dtm = System.DateTime.FromFileTimeUtc(time);
            return dtm.ToLocalTime();
        }
    }
    /// <summary>
    /// 当前文件长度,若为当前对象为文件则返回文件长度,若当前对象为目录则返回0
    /// </summary>
    public long FileLength
    {
        get
        {
            if (this.bolIsFile)
                return ToLong(myData.nFileSizeHigh, myData.nFileSizeLow);
            else
                return 0;
        }
    }

#endregion

#region 控制对象特性的一些属性 ****************************************

private bool bolThrowIOException = true;
    /// <summary>
    /// 发生IO错误时是否抛出异常
    /// </summary>
    public bool ThrowIOException
    {
        get { return this.bolThrowIOException; }
        set { this.bolThrowIOException = value; }
    }
    private bool bolReturnStringType = true;
    /// <summary>
    /// 是否以字符串方式返回查询结果,若返回true则当前对象返回为字符串,
    /// 否则返回 System.IO.FileInfo或System.IO.DirectoryInfo类型
    /// </summary>
    public bool ReturnStringType
    {
        get { return bolReturnStringType; }
        set { bolReturnStringType = value; }
    }
    
    private string strSearchPattern = "*";
    /// <summary>
    /// 要匹配的文件或目录名,支持通配符
    /// </summary>
    public string SearchPattern
    {
        get { return strSearchPattern; }
        set { strSearchPattern = value; }
    }
    private string strSearchPath = null;
    /// <summary>
    /// 搜索的父目录,必须为绝对路径,不得有通配符,该目录必须存在
    /// </summary>
    public string SearchPath
    {
        get { return strSearchPath; }
        set { strSearchPath = value; }
    }

private bool bolSearchForFile = true;
    /// <summary>
    /// 是否查找文件
    /// </summary>
    public bool SearchForFile
    {
        get { return bolSearchForFile; }
        set { bolSearchForFile = value; }
    }
    private bool bolSearchForDirectory = true;
    /// <summary>
    /// 是否查找子目录
    /// </summary>
    public bool SearchForDirectory
    {
        get { return bolSearchForDirectory; }
        set { bolSearchForDirectory = value; }
    }

#endregion

/// <summary>
    /// 关闭对象,停止搜索
    /// </summary>
    public void Close()
    {
        this.CloseHandler();
    }

#region IEnumerator 成员 **********************************************

/// <summary>
    /// 返回当前对象
    /// </summary>
    public object Current
    {
        get { return objCurrentObject ; }
    }
    /// <summary>
    /// 找到下一个文件或目录
    /// </summary>
    /// <returns>操作是否成功</returns>
    public bool MoveNext()
    {
        bool success = false;
        while (true)
        {
            if (this.bolStartSearchFlag)
                success = this.SearchNext();
            else
                success = this.StartSearch();
            if (success)
            {
                if (this.UpdateCurrentObject())
                    return true;
            }
            else
            {
                this.objCurrentObject = null;
                return false;
            }
        }
    }

/// <summary>
    /// 重新设置对象
    /// </summary>
    public void Reset()
    {
        if (this.strSearchPath == null)
            throw new System.ArgumentNullException("SearchPath can not null");
        if (this.strSearchPattern == null || this.strSearchPattern.Length == 0)
            this.strSearchPattern = "*";

this.intSearchedCount = 0;
        this.objCurrentObject = null;
        this.CloseHandler();
        this.bolStartSearchFlag = false;
        this.bolIsEmpty = false;
        this.intLastErrorCode = 0;
    }

#endregion

#region 声明WIN32API函数以及结构 **************************************

[Serializable,
    System.Runtime.InteropServices.StructLayout
        (System.Runtime.InteropServices.LayoutKind.Sequential,
        CharSet = System.Runtime.InteropServices.CharSet.Auto
        ),
    System.Runtime.InteropServices.BestFitMapping(false)]
    private struct WIN32_FIND_DATA
    {
        public int dwFileAttributes;
        public int ftCreationTime_dwLowDateTime;
        public int ftCreationTime_dwHighDateTime;
        public int ftLastAccessTime_dwLowDateTime;
        public int ftLastAccessTime_dwHighDateTime;
        public int ftLastWriteTime_dwLowDateTime;
        public int ftLastWriteTime_dwHighDateTime;
        public int nFileSizeHigh;
        public int nFileSizeLow;
        public int dwReserved0;
        public int dwReserved1;
        [System.Runtime.InteropServices.MarshalAs
            (System.Runtime.InteropServices.UnmanagedType.ByValTStr,
            SizeConst = 260)]
        public string cFileName;
        [System.Runtime.InteropServices.MarshalAs
            (System.Runtime.InteropServices.UnmanagedType.ByValTStr,
            SizeConst = 14)]
        public string cAlternateFileName;
    }

[System.Runtime.InteropServices.DllImport
        ("kernel32.dll",
        CharSet = System.Runtime.InteropServices.CharSet.Auto,
        SetLastError = true)]
    private static extern IntPtr FindFirstFile(string pFileName, ref WIN32_FIND_DATA pFindFileData);

[System.Runtime.InteropServices.DllImport
        ("kernel32.dll",
       CharSet = System.Runtime.InteropServices.CharSet.Auto,
        SetLastError = true)]
    private static extern bool FindNextFile(IntPtr hndFindFile, ref WIN32_FIND_DATA lpFindFileData);

[System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool FindClose(IntPtr hndFindFile);

private static long ToLong( int height , int low)
    {
        long v = ( uint ) height ;
        v = v << 0x20;
        v = v | ( ( uint )low );
        return v;
    }

private static void WinIOError(int errorCode, string str)
    {
        switch (errorCode)
        {
            case 80:
                throw new System.IO.IOException("IO_FileExists :" + str);
            case 0x57:
                throw new System.IO.IOException("IOError:" + MakeHRFromErrorCode(errorCode));
            case 0xce:
                throw new System.IO.PathTooLongException("PathTooLong:" + str );
            case 2:
                throw new System.IO.FileNotFoundException("FileNotFound:" + str);
            case 3:
                throw new System.IO.DirectoryNotFoundException("PathNotFound:" + str);
            case 5:
                throw new UnauthorizedAccessException("UnauthorizedAccess:" + str);
            case 0x20:
                throw new System.IO.IOException("IO_SharingViolation:" + str);
        }
        throw new System.IO.IOException("IOError:" + MakeHRFromErrorCode(errorCode));
    }

private static int MakeHRFromErrorCode(int errorCode)
    {
        return (-2147024896 | errorCode);
    }

#endregion

#region 内部代码群 ****************************************************

private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
    /// <summary>
    /// 查找处理的底层句柄
    /// </summary>
    private System.IntPtr intSearchHandler = INVALID_HANDLE_VALUE;

private WIN32_FIND_DATA myData = new WIN32_FIND_DATA();
    /// <summary>
    /// 开始搜索标志
    /// </summary>
    private bool bolStartSearchFlag = false;
    /// <summary>
    /// 关闭内部句柄
    /// </summary>
    private void CloseHandler()
    {
        if (this.intSearchHandler != INVALID_HANDLE_VALUE)
        {
            FindClose(this.intSearchHandler);
            this.intSearchHandler = INVALID_HANDLE_VALUE;
        }
    }
    /// <summary>
    /// 开始搜索
    /// </summary>
    /// <returns>操作是否成功</returns>
    private bool StartSearch()
    {
        bolStartSearchFlag = true;
        bolIsEmpty = false;
        objCurrentObject = null;
        intLastErrorCode = 0;

string strPath = System.IO.Path.Combine(strSearchPath, this.strSearchPattern);
        this.CloseHandler();
        intSearchHandler = FindFirstFile(strPath, ref myData);
        if (intSearchHandler == INVALID_HANDLE_VALUE)
        {
            intLastErrorCode = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
            if (intLastErrorCode == 2)
            {
                bolIsEmpty = true;
                return false;
            }
            if( this.bolThrowIOException )
                WinIOError( intLastErrorCode , strSearchPath);
            else
                return false;
        }
        return true;
    }
    /// <summary>
    /// 搜索下一个
    /// </summary>
    /// <returns>操作是否成功</returns>
    private bool SearchNext()
    {
        if (bolStartSearchFlag == false)
            return false;
        if (bolIsEmpty)
            return false;
        if (intSearchHandler == INVALID_HANDLE_VALUE)
            return false;
        intLastErrorCode = 0 ;
        if (FindNextFile(intSearchHandler, ref myData) == false)
        {
            intLastErrorCode = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
            this.CloseHandler();
            if (intLastErrorCode != 0 && intLastErrorCode != 0x12)
            {
                if (this.bolThrowIOException)
                    WinIOError(intLastErrorCode , strSearchPath);
                else
                    return false;
            }
            return false;
        }
        return true;
    }//private bool SearchNext()

/// <summary>
    /// 更新当前对象
    /// </summary>
    /// <returns>操作是否成功</returns>
    private bool UpdateCurrentObject()
    {
        if (intSearchHandler == INVALID_HANDLE_VALUE)
            return false;
        bool Result = false;
        this.objCurrentObject = null;
        if ((myData.dwFileAttributes & 0x10) == 0)
        {
            // 当前对象为文件
            this.bolIsFile = true;
            if (this.bolSearchForFile)
                Result = true;
        }
        else 
        {
            // 当前对象为目录
            this.bolIsFile = false;
            if (this.bolSearchForDirectory)
            {
                if (myData.cFileName == "." || myData.cFileName == "..")
                    Result = false;
                else
                    Result = true;
            }
        }
        if (Result)
        {
            if (this.bolReturnStringType)
                this.objCurrentObject = myData.cFileName;
            else
            {
                string p = System.IO.Path.Combine(this.strSearchPath, myData.cFileName);
                if (this.bolIsFile)
                {
                    this.objCurrentObject = new System.IO.FileInfo(p);
                }
                else
                {
                    this.objCurrentObject = new System.IO.DirectoryInfo(p);
                }
            }
            this.intSearchedCount++;
        }
        return Result;
    }//private bool UpdateCurrentObject()

#endregion

}//public class FileDirectoryEnumerator : System.Collections.IEnumerator

转载于:https://www.cnblogs.com/alexusli/archive/2008/10/07/1305548.html

C#使用WIN32API来高效率的遍历文件和目录(转)相关推荐

  1. File类遍历(文件夹)目录功能

    package com.learn.demo01.File;import java.io.File;/*File类遍历(文件夹)目录功能- public String[] list() :返回一个St ...

  2. php遍历视频文件,php使用glob函数遍历文件和目录详解

    php glob()函数返回匹配指定模式的文件名或目录.因此我们可以使用glob函数来查找文件,也可以实现目录的遍历. 函数说明:array glob ( string $pattern [, int ...

  3. php scandir 目录树,使用php scandir函数遍历文件夹目录和所有文件

    使用php scandir函数遍历文件夹目录和所有文件 $dir = "."; //当前目录 list_file($dir); function list_file($dir){ ...

  4. glob php,php使用glob函数遍历文件和目录详解

    php glob()函数返回匹配指定模式的文件名或目录.因此我们可以使用glob函数来查找文件,也可以实现目录的遍历. 函数说明:array glob ( string $pattern [, int ...

  5. 使用Dir函数遍历文件和目录

    版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的. 又翻出来很早前的一篇vb的文章,写的简单,没什么用,大家看看就可以了. Vb编程中,可以通过使用 ...

  6. python遍历文件_python3 遍历文件夹目录所有文件

    dic = { 'sum_size':0, 'file_num':0, 'directory_num':0 } def get_size(path,txt): items =os.listdir(pa ...

  7. Python高效率遍历文件夹寻找重复文件

    前言 为什么要写这篇文章呢...主要还是业务中有个需求,遍历一个将近200w数据的文件夹,大部分还都是视频文件那种,但是这玩意用的次数还不多,做文件夹index也不是很ok,所以写了一个脚本来处理这个 ...

  8. ASP.NET2.0 遍历文件夹下所有图片【月儿原创】

    ASP.NET2.0 遍历文件夹下所有图片 作者:清清月儿 主页:http://blog.csdn.net/21aspnet/           时间:2007.4.4 1.以下目录有若干图片(都是 ...

  9. OpenCV实现遍历文件夹下所有文件

    OpenCV中有实现遍历文件夹下所有文件的类Directory,它里面包括3个成员函数:(1).GetListFiles:遍历指定文件夹下的所有文件,不包括指定文件夹内的文件夹:(2).GetList ...

最新文章

  1. 深入探讨:视觉的目的是什么?
  2. hdu4829 带权并查集(题目不错)
  3. 用框架的你,可能早已忽略了这些事件API
  4. 转载 JDK + Android-SDK + Python + MonkeyRunner 的安装
  5. nginx mysql双机热备_MYSQL双机热备
  6. 大厂机密!30 个提升团队研发效能的锦囊
  7. Android 系统(185)---如何使用adb command来设置cpu频率和核数
  8. java 文件名 类名_java文件名为什么要与类名相同
  9. atoi java,leetcode题目8: 字符串转换整数 (atoi)(java)
  10. RFC 793翻译(TCP的主体内容)
  11. 应用多元统计分析第四章基于最小二乘估计线性回归分析python代码
  12. 【iOS 15】iPhone如何录屏?iPhone屏幕录制技巧分享
  13. django使用ldap进行用户登录认证
  14. 大学生充实自己生活的方法
  15. js实现点击保存图片
  16. bt linux wifi,Ubuntu安装无线网卡驱动Wifi/BT BCM4330(AP6383)
  17. 在线直播源码,VUE 获奖名单滚动显示的两种方式
  18. scada与MySQL连接_SCADA系统与实时数据库数据同步
  19. 快上车,老司机带你实现后台录像功能
  20. 毕业四个月,一个初出校门的程序员的生活现状

热门文章

  1. 一堆乱七八糟绝不正经的排序算法
  2. HBase安装与命令行操作
  3. 小编说之“常见问题答疑”
  4. 以太坊区块链Ethereum开发资料汇总
  5. ListView和数据适配器SimpleAdapter例子
  6. 职责链模式(Chain of Responsibility)(对象行为型)
  7. Android RelativeLayout属性
  8. 字符设备驱动程序 2
  9. (C++)寻找1-100以内所有素数,复杂度为O(nsqrt(n))与O(nloglogn)的两种方法
  10. 正则表达式最常用的符号匹配