04 其他
获取文件夹内的所有文件名称(含子-孙文件)、认识递归函数
在 Excel VBA 中,递归是指在函数内部再次调用自身的一种方法。它常用于解决"可分解为相似子问题的问题"(如遍历文件夹结构、计算阶乘等)。
递归编程的一个优点是程序代码变得更短、更优雅。此外,在某些情况下,递归调用是对其进行编程的唯一方法。
比如本章的示例,获取文件夹内的所有文件、包含子文夹内、孙文件夹内......,这是一个典型的树形结构,主干、枝干、细分枝干、在细分.....。
事先你并不知道文件夹内有多少个子孙文件夹的嵌套,也并不清楚一个文件夹内有多少个子文件夹的存在,就不能确定需要循环和嵌套循环的次数,所以调用递归函数来解决这个问题就变成了最优解!
本节关于递归函数过程和说明释义:
函数过程:
复制成功!
1
Sub 递归函数(路径 as string)
If 路径下有文件 Then
输出文件名称
End IF
If 路径下有文件夹 Then
输出文件夹名称(可选)
获取子文件夹的路径
Call 递归函数(新文件夹路径) '调用自身
End If
End Sub
释义说明:
文件夹嵌套:
运行视频:
源码下载:
描述:
获取某个文件夹下的所有文件名,包括所有子文件夹、孙文件夹......
样本示例:"豆包"安装路径下的文件
需求分析:
- 1、获取某个文件夹下的所有文件名称(含子、孙文件);
- 2、被获取的"根文件夹"可选择;
- 3、"文件名"和"文件夹名"读取到新的Excel表格,并标记是文件或文件夹;
- 4、为方便查看文件夹下的嵌套文件的结构,对"文件夹名"增加底色,相同路径下的文件放置到一起。
VBA实现过程:
- 1.把"可选择根文件夹"、"新建表格保存读取"、"增加底色、放置一起"等这些操作,放置到主程序中;
- 2.单独写一个递归函数(获取文件名称的实现过程)的子程序,参数包含路径、目标的工作表和起始行位置。在主程序中调用它;
- 3.主程序中的几个功能实现:
- ①Application.FileDialog(msoFileDialogFolderPicker),一个Application对象,用于在VBA中显示一个文件夹选择对话框,允许用户从文件系统中选择一个文件夹;
- ②ThisWorkbook.Sheets.Add,一个工作表方法,增加一个新的工作表,在示例中的命名是:文件列表 + 时间
- ③ActiveWindow.FreezePanes = True,冻结读取表的首行;
- ④.Interior.Color增加底色,这里用了一个内置方法.CurrentRegion.AutoFilter,设置数据筛选的属性,把所有"文件夹"的行筛选出来之后、增加底色;
- ⑤.Sort内置方法,对于相同路径的文件放置在一块,因为输出的文件和文件夹名称包含完整路径,排序即可实现,.Sort内置方法用来排序,key1增加排序条件列,Order1是升降序条件。
- 4.递归函数的注意事项:
- ①Dir(参数)函数:查找某个路径下文件或文件夹的方法,配合Do While...Loop循环,vbNormal参数表示文件,vbDirectory表示文件夹
- ②注意,因为一个文件夹中可能存在多个子文件夹,我们必须要把所有的子-孙文件夹的路径全部获取到,不遗漏任何文件夹。这里我们用一个Collection 集合,保存获取到的所有子文件夹的绝对路径,然后循环集合内的每个元素,获取该路径下的文件。这个非常重要。
- ③鉴于以上问题,我们可以把递归函数的过程分成三个实现不同功能的过程:
- a.获取文件名称;
- b.获取文件夹路径保存到集合;
- c.对集合元素遍历,调用递归函数,重复执行①和②,直到穷尽!
示例代码
复制成功!
1
Sub 获取指定文件夹内的所有文件()
Dim File_path_base As String
Dim Sheet_New As Worksheet
Dim Row_Count As Long
' 使用文件夹选择对话框,指定文件夹路径
With Application.FileDialog(msoFileDialogFolderPicker)
.Title = "请选择要遍历的文件夹"
.AllowMultiSelect = False '设置为 False ,表示文件对话框只允选择单个文件
If .Show = -1 Then '弹窗一个文件夹选择框
File_path_base = .SelectedItems(1) & "\" '获取指定的文件夹路径
Else
Exit Sub
End If
End With
' 设置输出位置(新建工作表,用当前日期"年月日时分秒"命名)
Set Sheet_New = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
Sheet_New.Name = "文件列表_" & Format(Now, "yyyymmdd_hhmmss")
Row_Count = 1 ' 初始化行计数器,从第一行开始
Sheet_New.Cells(Row_Count, 1) = File_path_base ' 添加根文件夹
Sheet_New.Cells(Row_Count, 2) = "根目录" ' 添加根文件夹
Row_Count = Row_Count + 1
'调用递归函数
Call TraverseFolder(File_path_base, Sheet_New, Row_Count) ' 调用递归函数
'整理获取文件夹及文件,按照根目录――根目录文件――子文件夹――子文件――孙文件夹――孙文件排序
With Sheet_New
.Rows("2:2").Select
ActiveWindow.FreezePanes = True '冻结首行(标题行)
End With
'排序
With Sheet_New.Range("A1").CurrentRegion '对工作表当前区域升序排序,包含标题行
.Sort key1:=.Range("A1"), Order1:=xlAscending, Header:=xlYes
End With
'"文件夹"增加底色
Sheet_New.Range("A1").CurrentRegion.AutoFilter Field:=2, Criteria1:="文件夹" '筛选,第二列中包含"文件夹"的行
With Sheet_New.Range("A1").CurrentRegion
.Interior.Color = RGB(180, 180, 180) '增加底色
.Font.Bold = True '字体加粗
.AutoFilter '取消筛选
End With
Sheet_New.Columns("A:A").EntireColumn.AutoFit '自动调整列宽
End Sub
' 递归函数
Sub TraverseFolder(File_Path As String, Sheet_New As Worksheet, ByRef rowNum As Long)
Dim File_Name As String
Dim File_Name_son As String
Dim Full_Path As String
Dim Files_Col As Collection
Set Files_Col = New Collection ' 创建集合,存储子文件夹
'----------------------------------------------------------------------------------------
' 步骤1: 获取当前文件夹下的所有文件
File_Name = Dir(File_Path & "*", vbNormal) 'vbNormal类型,单指文件
Do While File_Name <> ""
If File_Name <> "." And File_Name <> ".." Then '跳过系统目录
Full_Path = File_Path & File_Name
If (GetAttr(Full_Path) And vbDirectory) = 0 Then ' 确保是文件
Sheet_New.Cells(rowNum, 1) = Full_Path '写入文件名
Sheet_New.Cells(rowNum, 2) = "文件" '写入文件类型
rowNum = rowNum + 1
End If
End If
File_Name = Dir() '查找其他文件
Loop
'----------------------------------------------------------------------------------------
' 步骤2: 获取所有子文件夹并存储到集合
File_Name_son = Dir(File_Path, vbDirectory) 'vbDirectory,单指文件夹
Do While File_Name_son <> ""
If File_Name_son <> "." And File_Name_son <> ".." Then '跳过系统目录
Full_Path = File_Path & File_Name_son
If (GetAttr(Full_Path) And vbDirectory) = vbDirectory Then
Files_Col.Add Full_Path & "\" ' 如果是子文件夹,先添加到集合中存储
Sheet_New.Cells(rowNum, 1) = Full_Path & "\" '写入文件夹名称
Sheet_New.Cells(rowNum, 2) = "文件夹" '写入文件类型
rowNum = rowNum + 1
End If
End If
File_Name_son = Dir() '查找其他文件夹
Loop
'----------------------------------------------------------------------------------------
' 步骤3: 递归处理所有子文件夹
Dim i As Long
For i = 1 To Files_Col.Count
Call TraverseFolder(Files_Col(i), Sheet_New, rowNum) '调用递归处理
Next i
Set Files_Col = Nothing ' 清理集合
End Sub
请开发者喝杯咖啡!