ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

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

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

    [复制链接]

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-2-4 11:00 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
本帖已被收录到知识树中,索引项:文件操作和FSO
本帖最后由 香川群子 于 2015-2-4 12:27 编辑

接下来,马不停蹄,向大家介绍标准的Dir搜寻文件、子文件夹的方法。
  1. Sub ListAllDirDicTest()
  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 Join(ListAllDirDic(myPath), vbCr) 'GetAllSubFolder's File 列出目标文件夹内含子文件夹内所有文件
  8.     MsgBox Join(ListAllDirDic(myPath, 1), vbCr) 'GetThisFolder's File 列出目标文件夹内所有文件(不含子文件夹)
  9.    
  10.     MsgBox Join(ListAllDirDic(myPath, -1), vbCr) 'GetThisFolder's SubFolder 仅列出目标文件夹内的子文件夹
  11.     MsgBox Join(ListAllDirDic(myPath, -2), vbCr) 'GetAllSubFolder 列出目标文件夹内含子文件夹的所有子文件夹
  12.    
  13.     MsgBox Join(ListAllDirDic(myPath, 1, "tst"), vbCr) 'GetThisFolder's SpecialFile 仅列出文件夹内含关键字文件
  14.     MsgBox Join(ListAllDirDic(myPath, , "tst"), vbCr) 'GetAllSubFolder's SpecialFile 列出子文件夹内含关键字文件
  15.    
  16. End Sub

  17. Function ListAllDirDic(myPath$, Optional sb& = 0, Optional SpFile$ = "")
  18.     '利用Dir方法、以及用2个字典分别记录子文件夹路径和文件名的文件搜寻方法。

  19.     '第1参数【指定路径myPath】必选 为指定目标文件夹的绝对路径

  20.     '第2参数【子文件夹模式sb】为可选 =奇数时只搜寻当前文件夹、=偶数时搜寻所有子文件夹
  21.     '                                      该参数>=0时返回文件名、<0时返回文件夹路径名
  22.     '因此事实上第2参数可以设置这样四种模式:
  23.     '  默认=0时,搜寻所有子文件夹并返回所有文件名
  24.     '        =1时,搜寻当前文件夹并返回所有文件名 (不向下搜寻子文件夹)
  25.     '        =-1时,搜寻当前文件夹并返回子文件夹路径名
  26.     '        =-2时, 搜寻所有子文件夹并返回所有子文件夹路径名

  27.     '第3参数【文件名指定特殊匹配字符SpFile】 可选,返回文件名时用此关键词过滤一下
  28.     '默认留空时,返回全部文件名 (等于没有被过滤掉)
  29.     ' = 某个关键字时,返回符合匹配(即含该关键字)的部分文件名 (有过滤掉不含关键字的文件名)
  30.     ' = .xl 也可这样指定文件类型,返回匹配(该关键字指定文件类型)的部分文件名 (过滤掉其它类型文件名)
  31.    
  32.     Dim i&, j&, myFile$
  33.     Set d1 = CreateObject("Scripting.Dictionary") '定义存放子文件夹路径的字典d1
  34.     Set d2 = CreateObject("Scripting.Dictionary") '定义存放文件名的字典d2
  35.    
  36.     d1(myPath) = " '字典d1初始化记录目标文件夹路径名
  37.     On Error Resume Next
  38.     Do While i < d1.Count
  39.         kr = d1.Keys  '从字典d1中更新提取所有子文件夹
  40.         myFile = Dir(kr(i), vbDirectory) '用Dir方法遍历该子文件夹得到文件或文件夹名 注意从kr(i)开始避免重复
  41.         Do While myFile <> "" 'Dir遍历直到返回空字符串 (即无未被遍历的文件或文件夹了)
  42.             If myFile <> "." And myFile <> ".." Then '如果是"."或".."属性则不用处理
  43.                 If (GetAttr(kr(i) & myFile) And vbDirectory) = vbDirectory Then '判断是文件夹属性时
  44.                     If Err.Number Then Err.Clear Else d1(kr(i) & myFile & "") = ""
  45.                     '#52 文件名Err时忽略(一般为操作系统语言文字环境问题),否则字典d1记录该子文件夹路径
  46.                 Else '如果不是文件夹则为文件
  47.                     If SpFile = "" Then '如未指定关键字
  48.                         j = j +1: d2(j) = myFile '则所有文件名都作为Item项加入字典d2 (不能使用key防止重名文件)
  49.                     Else '否则指定了关键字
  50.                         If InStr(myFile, SpFile) Then j = j +1: d2(j) = myFile
  51.                         '则判断含有指定关键字以后才可作为Item项加入字典d2 (不能使用key防止重名文件)
  52.                     End If
  53.                 End If
  54.             End If
  55.             myFile = Dir '用Dir方法继续搜寻下一个文件或子文件夹
  56.         Loop
  57.         If sb Mod 2 Then Exit Do Else i = i + 1
  58.         '如果第2参数指定为奇数则不用继续检查子文件夹就可退出,
  59.         '否则 i+1避免重复检查然后利用字典d1中的记录,继续检查下一个子文件夹直到全部子文件夹检查完毕
  60.     Loop
  61.     If sb >= 0 Or Len(SpFile) Then ListAllDirDic = d2.Items Else ListAllDirDic = d1.Keys
  62.     '如果第2参数>=0或第3参数有指定则返回d2的Items文件名、否则返回d1的keys子文件夹名
  63. End Function
复制代码
呵呵。

评分

6

查看全部评分

TA的精华主题

TA的得分主题

发表于 2015-2-4 11:11 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
收藏,学习!!

TA的精华主题

TA的得分主题

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

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-2-4 12:16 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
其实不用字典也可以。但是要使用Redim数组,并不断地更新数组大小……这让代码看上去有点烦。

解释暂略(因为很上面一样的)
  1. Sub ListAllDirTest()
  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 Join(ListAllDir(myPath), vbCr) 'GetAllSubFolder's File
  8.     MsgBox Join(ListAllDir(myPath, 1), vbCr) 'GetThisFolder's File
  9.    
  10.     MsgBox Join(ListAllDir(myPath, -1), vbCr) 'GetThisFolder's SubFolder
  11.     MsgBox Join(ListAllDir(myPath, -2), vbCr) 'GetAllSubFolder
  12.    
  13.     MsgBox Join(ListAllDir(myPath, 1, "tst"), vbCr) 'GetThisFolder's SpecialFile
  14.     MsgBox Join(ListAllDir(myPath, , "tst"), vbCr) 'GetAllSubFolder's SpecialFile
  15.    
  16. End Sub

  17. Function ListAllDir(myPath$, Optional sb& = 0, Optional SpFile$ = "")
  18.     Dim i&, j&, k&, myFile$
  19.     ReDim fld(0), file(0) '定义可变数组fld存放子文件夹路径、file存放文件名
  20.    
  21.     fld(0) = myPath '子文件夹初始化写入指定目标文件夹路径
  22.     On Error Resume Next
  23.     Do
  24.         myFile = Dir(fld(i), vbDirectory)
  25.         Do While myFile <> ""
  26.             If myFile <> "." And myFile <> ".." Then
  27.                 If (GetAttr(fld(i) & myFile) And vbDirectory) = vbDirectory Then
  28.                     If Err.Number Then Err.Clear Else j = j + 1: ReDim Preserve fld(j): fld(j) = fld(i) & myFile & ""
  29.                 Else
  30.                     If SpFile = "" Then
  31.                         file(k) = myFile: k = k + 1: ReDim Preserve file(k)
  32.                     Else
  33.                         If InStr(myFile, SpFile) Then file(k) = myFile: k = k + 1: ReDim Preserve file(k)
  34.                     End If
  35.                 End If
  36.             End If
  37.             myFile = Dir
  38.         Loop
  39.         If sb Mod 2 Then Exit Do Else i = i + 1
  40.     Loop Until i > UBound(fld)
  41.     If sb >= 0 Or Len(SpFile) Then ListAllDir = file Else ListAllDir = fld
  42. End Function
复制代码

TA的精华主题

TA的得分主题

发表于 2015-2-4 12:27 | 显示全部楼层
香川群子 发表于 2015-2-4 12:16
其实不用字典也可以。但是要使用Redim数组,并不断地更新数组大小……这让代码看上去有点烦。

解释暂略( ...

老师的代码太高深了,慢慢学习。

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-2-4 12:42 | 显示全部楼层
注意使用字典时,对于【子文件】来说因为是以绝对路径为key所以不用担心有重复。(字典本身也可以去重复)

但是,对于文件名来说,必须考虑在不同的文件夹中存在大量的同名文件,
所以不能直接用文件名作为字典key储存,必须使用计数序号j 作为keys,把文件名作为Item项存入字典,
才能避免文件名重复时不被字典错误覆盖。

呵呵。这个是细节,但是很重要。

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2015-2-4 12:45 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
收藏学习,谢谢精彩分享

TA的精华主题

TA的得分主题

发表于 2015-2-4 12:45 | 显示全部楼层
香川群子 发表于 2015-2-4 12:42
注意使用字典时,对于【子文件】来说因为是以绝对路径为key所以不用担心有重复。(字典本身也可以去重复)
...

这点要注意,谢谢提醒!

TA的精华主题

TA的得分主题

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

最后,作为本帖的特色,介绍使用VBA语句直接调用Dos中Dir命令来搜寻文件名的方法:
  1. Sub ListFilesDos()
  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.    
  5.     myFile$ = InputBox("Filename", "Find File", ".xl")
  6.     '在这里输入需要指定的关键字,可以是文件名的一部分,或指定文件类型如 ".xl"
  7.     tms = Timer
  8.     With CreateObject("Wscript.Shell") 'VBA调用Dos命令
  9.         ar = Split(.exec("cmd /c dir /a-d /b /s " & Chr(34) & myPath & Chr(34)).StdOut.ReadAll, vbCrLf) '所有文档含子文件夹
  10.         '指定Dos中Dir命令的开关然后提取结果 为指定文件夹以及所含子文件夹内的所有文件的含路径全名。
  11.         s = "from " & UBound(ar) & " Files by Search time: " & Format(Timer - tms, " 0.00s") & " in: " & myPath
  12.         '记录Dos中执行Dir命令的耗时
  13.         tms = Timer: ar = Filter(ar, myFile) '然后开始按指定关键词进行筛选。可筛选文件名或文件类型
  14.         Application.StatusBar = Format(Timer - tms, "0.00s") & " Find " & UBound(ar) + IIf(myFile = "", 0, 1) & " Files " & s
  15.         '在Excel状态栏上显示执行结果以及耗时
  16.     End With
  17.     [a:a] = "": If UBound(ar) > -1 Then [a2].Resize(1 + UBound(ar)) = WorksheetFunction.Transpose(ar)
  18.     '清空A列,然后输出结果
  19. End Sub
复制代码
呵呵,Dos命令不仅简洁,而且高效。

追加更正:提去文件个数统计
提取文件结果的数组ar是下标 0开始的1维数组,元素个数应该=UBound(ar)+1 【此处修正+1为ar(0)】
但实际未产生筛选时的文件结果数=UBound(ar) 无需+1 【因为Dos提取时Dir最后1个""也在结果之中】
而当指定筛选参数myFile不为空时,即产生实际筛选以后的数组ar中会排除最后的那个"",所以筛选后的统计文件结果数=UBound(ar) + 1

点评

这段代码看着眼熟的紧......  发表于 2015-2-7 13:55

评分

5

查看全部评分

TA的精华主题

TA的得分主题

发表于 2015-2-4 13:02 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
这个好!
致敬!
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

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

GMT+8, 2024-11-22 06:57 , Processed in 0.041065 second(s), 7 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

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

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

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