ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 190027|回复: 390

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

    [复制链接]

TA的精华主题

TA的得分主题

发表于 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方法 是综合性能最好的。 其实掌握基本概念以后非常好用。 递归、字典、数组 三个方法的代码,请大家各取所需灵活运用。


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




评分

85

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-2-4 09:14 | 显示全部楼层
首先要介绍,在VBA代码运行以后,调用【目标文件夹】的方法:

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

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

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


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

呵呵。这是本帖第一宝。

评分

9

查看全部评分

TA的精华主题

TA的得分主题

发表于 2015-2-4 09:22 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
香川群子 发表于 2015-2-4 09:14
首先要介绍,在VBA代码运行以后,调用【目标文件夹】的方法:

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

香川老师的作品都是精品。虽然不懂VBA,先收藏了,以备后用。

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-2-4 09:32 | 显示全部楼层
接下来,直接介绍当前流行的、高大上的FSO方法。

由简到繁地介绍:

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

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

  10. Function ListFiles(myPath$)
  11.    Set fso = CreateObject("Scripting.FileSystemObject") '打开FSO脚本、建立FSO对象实例
  12.    For Each f In fso.GetFolder(myPath).Files  '用FSO方法遍历指定文件夹内所有文件
  13.       i = i + 1: s = s & vbCr & f.Name            '逐个列出文件名并统计文件个数 i
  14.    Next
  15.    ListFiles = i & " Files:" & s  '返回所有文件名的合并字符串
  16. 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只是其中的一种属性=文件名。

评分

3

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-2-4 09:38 | 显示全部楼层
二、仅列出目标文件夹中所有子文件夹名。(不包括目标文件夹中文件、不包括子文件夹中的文件或子文件夹)
  1. Sub ListFilesTest()
  2.     With Application.FileDialog(msoFileDialogFolderPicker)
  3.         If .Show Then myPath$ = .SelectedItems(1) Else Exit Sub
  4.     End With
  5.     If Right(myPath, 1) <> "" Then myPath = myPath & ""
  6.    
  7.     MsgBox ListFolders(myPath)
  8.    
  9. End Sub
  10. Function ListFolders(myPath$)
  11.    Set fso = CreateObject("Scripting.FileSystemObject")
  12.    For Each f In fso.GetFolder(myPath).SubFolders
  13.       j = j + 1: t = t & vbCr & f.Name
  14.    Next
  15.    ListFolders = j & " Folders:" & t
  16. End Function
复制代码
和楼上的代码ListFiles相比,差异很小,仅在于:
fso.GetFolder(myPath).Files
fso.GetFolder(myPath).SubFolders

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


呵呵。是不是特别简单那?

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-2-4 09:51 | 显示全部楼层
本帖最后由 香川群子 于 2015-2-4 09:52 编辑

下面很快进入正题:

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

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

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

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

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


TA的精华主题

TA的得分主题

发表于 2015-2-4 10:10 | 显示全部楼层

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-2-4 10:10 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
本帖最后由 香川群子 于 2015-2-5 09:42 编辑

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

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

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

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

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

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

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

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

请大家慢慢消化吸收,可以自己任意修改,试一试会得到什么……这样才有进步!

评分

5

查看全部评分

TA的精华主题

TA的得分主题

发表于 2015-2-4 10:37 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2015-2-4 10:58 | 显示全部楼层
很好的帖子,但是建议老师写个附件。呵呵!

点评

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

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-11-22 07:15 , Processed in 0.039230 second(s), 8 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表