ExcelHome技术论坛

 找回密码
 免费注册
QQ登录 只需一步,快速开始
新浪微博登陆 只需一步, 快速开始
   
高效办公必会的Office99uu优优 永久免费,网表让Excel秒变数据库 Excel服务器-会Excel,做管理系统 Excel Home精品图文教程库
Excel不给力? 何不试试FoxTable! 国内首部Excel函数公式学习大典 职场充电黑科技, Office微99uu优优 免费下载Excel行业应用视频
300集Office 2010微99uu优优 Tableau-数据可视化工具 突破Excel限制,用活字格提高效率 12门Excel免费公开课任你学
你的Excel 201099uu优优学习锦囊 欲罢不能, 过目难忘的 Office 新界面 免费的Excel考勤计算系统
查看: 43736|回复: 232
打印 上一主题 下一主题

[分享] 跟我学 【喜迎2015立春】遍历文件夹(含子文件夹)方法 ABC

  [复制链接]
TA的精华主题TA的得分主题
跳转到指定楼层
1
发表于 2015-2-4 08:53 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖已被收录到知识树中,索引项:文件操作和FSO
本帖最后由 香川群子 于 2015-2-5 09:37 编辑

遍历文件夹内所有文件、
遍历文件夹内所有指定类型的文件、
遍历文件夹内所有指定名称的文件、

遍历文件夹内所有子文件夹、

遍历文件夹以及所有子文件夹内所有文件、
遍历文件夹以及所有子文件夹内所有指定类型的文件、
遍历文件夹以及所有子文件夹内所有指定名称的文件、

…………
上面的要求,是一个永恒的话题。
尤其是新手VBA初学者,往往会在不同阶段开始涉及此类要求。

…………
EH论坛中搜索一下,会有一大堆的实际应用例子。
也早已有热心的高手,做了代码优化……写成了标准的自定义函数或过程。

做法基本上有2大类:
① 经典Dir
② FSO
即 Scripting.FileSystemObject 的【文件系统对象】脚本方法。

一开始可能Dir方法较为普遍,但随着水平的提高,应用FSO方法因为有更多的好处而更为流行。

我会在本帖详细介绍这二种方法、并且是适合初学者的、循序渐进的方法。
以便大家迅速掌握,并消化吸收以后进入自己的知识库。

最后,我还会向大家介绍第三种方法:在VBA中使用Dos的Dir命令的高效遍历子文件夹中所有指定文件的方法。

由于有这个特色,所以我觉得单独开帖是有必要的,我的这个【遍历子文件夹方法】的帖子,将成为经典。
补充:由于Application.FileSearch方法仅能用于2003版,以后的版本不再能使用此方法,所以就无需介绍了。



代码介绍完毕,初步总结一下:
① Dos Dir 方法最快,但是只能提取全部文件、文件夹名后输出到工作表,无法在中途进行具体文件的选择处理。
② VBA Dir 方法比较容易学习、理解。(简单的Do 循环即可) 但中间需要判断区分文档和文件夹,代码结构稍复杂。
③ FSO方法 是综合性能最好的。 其实掌握基本概念以后非常好用。 递归、字典、数组 三个方法的代码,请大家各取所需灵活运用。


对本帖有任何问题或要求,欢迎随时跟帖、提问。




评分

参与人数 44财富 +180 鲜花 +86 技术 +2 收起 理由
ljpmqb888 + 2 太强大了
chenrh007 + 2 太强大了
三流高手 + 2 太强大了
Amarry + 2 优秀作品
xihai + 2 优秀作品
查看全部评分
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏310 分享分享 有用有用30 无用无用 分享到新浪微博
TA的精华主题TA的得分主题
2
 楼主| 发表于 2015-2-4 09:14 | 只看该作者 |楼主
首先要介绍,在VBA代码运行以后,调用【目标文件夹】的方法:

① 微软Excel VBA 默认选择文件夹的Dialog对话框
  • Sub ListFilesTest()
  •     With Application.FileDialog(msoFileDialogFolderPicker) '运行后出现标准的选择文件夹对话框        
  •         If .Show Then myPath = .SelectedItems(1) Else Exit Sub '如选中则返回=-1 / 取消未选则返回=0
  •     End With
  •     If Right(myPath, 1) <> "" Then myPath = myPath & ""
  •     '返回的是选中目标文件夹的绝对路径,但除了本地C盘、D盘会以"C:"形式返回外,其余路径无""需要自己添加
  • End Sub
  • 复制代码
    ② 视窗浏览器界面选择目标文件夹
  • Sub ListFilesTest()
  •     Set myFolder = CreateObject("Shell.Application").BrowseForFolder(0, "GetFolder", 0)
  •     If Not myFolder Is Nothing Then myPath$ = myFolder.Items.Item.Path Else MsgBox "Folder not Selected": Exit Sub
  •     If Right(myPath, 1) <> "" Then myPath = myPath & ""
  •     '同样返回的是选中目标文件夹的绝对路径,但除了本地C盘、D盘会以"C:"形式返回外,其余路径无""需要添加

  • End Sub
  • 复制代码
    …………
    这两种选择目标文件夹的方法,总的效果应该都不错。
    方法-1 默认Dialog对话框左侧栏有桌面、我的文档等快捷方式,也比较符合一般人的使用习惯。
    优点是,本层文件夹内的子文件夹全部以大图标方式列出(也可以改为列表)看起来较为轻松。
    缺点是,如果有多层子文件夹,需要一层一层地点下去……似乎比较累一点。

    与此相对、方法-2 是浏览器形式,点击+号可以展开、点击-号可以折叠。
    因此也有很多人特别喜欢这一种的,尤其是有多层子文件夹时很方便。


    …………
    因此,这两种方法你喜欢哪一种都可以,可以多试几次然后定下来。

    呵呵。这是本帖第一宝。

    评分

    参与人数 3鲜花 +6 收起 理由
    hcy1185 + 2 我喜欢!运行代码弹出选择盘符或文件夹的窗.
    shanchuan + 2 优秀作品
    唐伯狼 + 2 优秀作品
    查看全部评分
    TA的精华主题TA的得分主题
    3
    发表于 2015-2-4 09:22 | 只看该作者
    香川群子 发表于 2015-2-4 09:14
    首先要介绍,在VBA代码运行以后,调用【目标文件夹】的方法:

    ① 微软Excel VBA 默认选择文件夹的Dialog ...

    香川老师的作品都是精品。虽然不懂VBA,先收藏了,以备后用。
    TA的精华主题TA的得分主题
    4
     楼主| 发表于 2015-2-4 09:32 | 只看该作者 |楼主
    接下来,直接介绍当前流行的、高大上的FSO方法。

    由简到繁地介绍:

    一、仅列出目标文件夹中所有文件。(不包括 子文件夹、不包括子文件夹中的文件)
  • Sub ListFilesTest()
  •     With Application.FileDialog(msoFileDialogFolderPicker)
  •         If .Show Then myPath$ = .SelectedItems(1) Else Exit Sub
  •     End With
  •     If Right(myPath, 1) <> "" Then myPath = myPath & ""
  •     '以上选择目标文件夹以得到路径myPath

  •     MsgBox ListFiles(myPath)    '调用FSO的ListFiles过程返回目标文件夹下的所有文件名
  •    
  • End Sub

  • Function ListFiles(myPath$)
  •    Set fso = CreateObject("Scripting.FileSystemObject") '打开FSO脚本、建立FSO对象实例
  •    For Each f In fso.GetFolder(myPath).Files  '用FSO方法遍历指定文件夹内所有文件
  •       i = i + 1: s = s & vbCr & f.Name            '逐个列出文件名并统计文件个数 i
  •    Next
  •    ListFiles = i & " Files:" & s  '返回所有文件名的合并字符串
  • End Function
  • 复制代码
    本代码只是一个简单的示例,大家理解以后,就可以改编为任何自己希望的操作代码,
    实现对指定目标文件夹内所有文件的遍历。

    …………怎么样?特别容易、特别简单吧?

    知识介绍:
    Set fso = CreateObject("Scripting.FileSystemObject")
    建立FSO 即【文件系统对象】的实例。

    这以后,即可简单、直接地引用fso的各种属性(有时间可以自己慢慢研究)

    For Each f In fso.GetFolder(myPath).Files
    '用FSO方法遍历指定文件夹内所有文件

    fso.GetFolder(myPath) 是指对于路径myPath,使用FSO对象方法得到其文件夹.GetFolder属性
    然后,对于这个指定的目标文件夹,继续返回其所有文件的属性、即.Files
    完整的部分为:  fso.GetFolder(myPath).Files

    然后,对于这个所有文件的集合即 fso.GetFolder(myPath).Files
    通过For……Each循环就可以遍历其中每一个文件了。

    具体地,For Each f In 中的f变量,即为每一个文件。
    循环中,可以使用f的各种属性。 f.Name只是其中的一种属性=文件名。

    评分

    参与人数 2鲜花 +4 收起 理由
    Pan嗯嗯 + 2 太强大了
    autumnalRain + 2 优秀作品
    查看全部评分
    TA的精华主题TA的得分主题
    5
     楼主| 发表于 2015-2-4 09:38 | 只看该作者 |楼主
    二、仅列出目标文件夹中所有子文件夹名。(不包括目标文件夹中文件、不包括子文件夹中的文件或子文件夹)
  • Sub ListFilesTest()
  •     With Application.FileDialog(msoFileDialogFolderPicker)
  •         If .Show Then myPath$ = .SelectedItems(1) Else Exit Sub
  •     End With
  •     If Right(myPath, 1) <> "" Then myPath = myPath & ""
  •    
  •     MsgBox ListFolders(myPath)
  •    
  • End Sub
  • Function ListFolders(myPath$)
  •    Set fso = CreateObject("Scripting.FileSystemObject")
  •    For Each f In fso.GetFolder(myPath).SubFolders
  •       j = j + 1: t = t & vbCr & f.Name
  •    Next
  •    ListFolders = j & " Folders:" & t
  • End Function
  • 复制代码
    和楼上的代码ListFiles相比,差异很小,仅在于:
    fso.GetFolder(myPath).Files
    fso.GetFolder(myPath).SubFolders

    即,把目标文件夹fso.GetFolder(myPath)的属性,
    有.Files 所有文件、改为 .SubFolders 所有子文件夹


    呵呵。是不是特别简单那?
    TA的精华主题TA的得分主题
    6
     楼主| 发表于 2015-2-4 09:51 | 只看该作者 |楼主
    本帖最后由 香川群子 于 2015-2-4 09:52 编辑

    下面很快进入正题:

    三、遍历目标文件夹内所有文件、以及所有子文件夹中的所有文件。

    以下代码仅为示例,可以用,但代码粗糙不足以成为实用程序。
    但是可以在此基础上修改为各种可能。
  • Sub ListFilesTest()
  •     With Application.FileDialog(msoFileDialogFolderPicker)
  •         If .Show Then myPath$ = .SelectedItems(1) Else Exit Sub
  •     End With
  •     If Right(myPath, 1) <> "" Then myPath = myPath & ""
  •    
  •     [a:a] = ""                    '清空A列
  •     Call ListAllFso(myPath)   '调用FSO遍历子文件夹的递归过程
  •    
  • End Sub

  • Function ListAllFso(myPath$) '用FSO方法遍历并列出所有文件和文件夹名的【递归过程】
  •     Set fld = CreateObject("Scripting.FileSystemObject").GetFolder(myPath)
  •     '用FSO方法得到当前路径的文件夹对象实例 注意这里的【当前路径myPath是个递归变量】

  •     For Each f In fld.Files  '遍历当前文件夹内所有【文件.Files】
  •         [a65536].End(3).Offset(1) = f.Name '在A列逐个列出文件名
  •     Next

  •     For Each fd In fld.SubFolders  '遍历当前文件夹内所有【子文件夹.SubFolders】
  •         [a65536].End(3).Offset(1) = " " & fd.Name & ""  '在A列逐个列出子文件夹名
  •         Call ListAllFso(fd.Path)       '注意此时的路径变量已经改变为【子文件夹的路径fd.Path】
  •         '注意重点在这里: 继续向下调用递归过程【遍历子文件夹内所有文件文件夹对象】
  •     Next
  • End Function
  • 复制代码
    其实、递归是个让代码更简单的好工具、好算法。
    因为它可以把相同的过程的代码反复引用而无需重复写出来。……建议不熟悉递归的开始研究一下递归算法。


    TA的精华主题TA的得分主题
    7
    发表于 2015-2-4 10:10 | 只看该作者
    TA的精华主题TA的得分主题
    8
     楼主| 发表于 2015-2-4 10:10 | 只看该作者 |楼主
    本帖最后由 香川群子 于 2015-2-5 09:42 编辑

    由于很多初学者不太能理解递归算法的过程而产生畏难、抵触情绪,
    所以下面避开递归,而采用字典记录中间结果的方法,同样来达到遍历所所有子文件的目的:
  • Sub ListFilesTest()
  •     With Application.FileDialog(msoFileDialogFolderPicker)
  •         If .Show Then myPath$ = .SelectedItems(1) Else Exit Sub
  •     End With
  •     If Right(myPath, 1) <> "" Then myPath = myPath & ""
  •    
  •     MsgBox "List Files:" & vbCr & Join(ListAllFsoDic(myPath), vbCr)
  •     MsgBox "List SubFolders:" & vbCr & Join(ListAllFsoDic(myPath, 1), vbCr)
  • End Sub

  • Function ListAllFsoDic(myPath$, Optional k = 0) '使用2个字典但无需递归的遍历过程
  •     Dim i&, j&
  •     Set d1 = CreateObject("Scripting.Dictionary") '字典d1记录子文件夹的绝对路径名   
  •     Set d2 = CreateObject("Scripting.Dictionary") '字典d2记录文件名 (文件夹和文件分开处理)

  •     d1(myPath) = ""           '以当前路径myPath作为起始记录,以便开始循环检查

  •     Set fso = CreateObject("Scripting.FileSystemObject")
  •     Do While i < d1.Count
  •     '当字典1文件夹中有未遍历处理的key存在时进行Do循环 直到 i=d1.Count即所有子文件夹都已处理时停止

  •         kr = d1.Keys '取出文件夹中所有的key即所有子文件夹路径 (注意每次都要更新)
  •         For Each f In fso.GetFolder(kr(i)).Files '遍历该子文件夹中所有文件 (注意仅从新的kr(i) 开始)
  •             j = j + 1: d2(j) = f.Name
  •            '把该子文件夹内的所有文件名作为字典Item项加入字典d2 (为防止文件重名不能用key属性)
  •         Next

  •         i = i + 1 '已经处理过的子文件夹数目 i +1 (避免下次产生重复处理)
  •         For Each fd In fso.GetFolder(kr(i - 1)).SubFolders '遍历该文件夹中所有新的子文件夹
  •             d1(fd.Path) = " " & fd.Name & ""
  •             '把新的子文件夹路径存入字典d1以便在下一轮循环中处理
  •         Next
  •     Loop

  •     If k Then ListAllFsoDic = d1.Keys Else ListAllFsoDic = d2.Items
  •     '如果参数=1则列出字典d1中所有子文件夹的路径名 (如使用d1.Items则仅列出子文件夹名称不含路径)
  •     '如果参数=0则默认列出字典d2中Items即所有文件名

  • End Function
  • 复制代码
    FSO方法到此暂且告一段落(以后我会再贴出较为实用的代码)

    请大家慢慢消化吸收,可以自己任意修改,试一试会得到什么……这样才有进步!
    TA的精华主题TA的得分主题
    9
    发表于 2015-2-4 10:37 | 只看该作者
    TA的精华主题TA的得分主题
    10
    发表于 2015-2-4 10:58 | 只看该作者
    很好的帖子,但是建议老师写个附件。呵呵!

    点评

    我的代码是通用的,给你的代码你复制一下就能用在任意文件里了,不需要特别的附件。  发表于 2015-2-4 13:16
    您需要登录后才可以回帖 登录 | 免费注册 新浪微博登陆
    本版积分规则
    关闭

    最新热点上一条 /1 下一条

    关注官方微信,每天坐享新鲜教程
    手机版|关于我们|联系我们|ExcelHome    GMT+8, 2017-11-19 16:50 , Processed in 0.103976 second(s), 18 queries , Gzip On, MemCache On.
    Powered by Discuz! X3.3 © 2001-2017 Wooffice Inc.
        沪公网安备 31011702000001号 沪ICP备11019229号 本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:徐怀玉律师 李志群律师
    快速回复 返回顶部 返回列表
    99uu优优