该函数返回一个 String(字符串),用以表示一个文件名、目录名或文件夹名称,它必须与指定的模式或文件属性、或磁盘卷标相匹配。
语法:
Dir[(pathname[, attributes])]
Dir 函数的语法具有以下几个部分:
部分 | 描述 |
pathname | 用来指定文件名的字符串表达式,可能包含目录或文件夹、以及驱动器。如果没有找到 pathname,则会返回零长度字符串 ("")。 在 Microsoft Windows 中, Dir 支持多字符 (*) 和单字符 (?) 的通配符来指定多重文件。 |
attributes | 可选参数。常数或数值表达式,其总和用来指定文件属性。如果省略,则会返回匹配 pathname 但不包含属性的文件。 |
Dir函数的两个参数都是可选的,pathname是文件或文件夹名称,对于参数attributes本教程只介绍常用的 vbDirectory 属性。
接下来,以遍历下图文件夹数据的过程为例来演示一下Dir函数的用法。
C:\Users\Administrator\Desktop\vba\demo
│ 01 (1).xlsx
│ 01 (2).xlsx
│ 文件夹改名.xls
│ 新建文本文档.txt
├─1601
│ 430Forbidden.png
├─1603
│ 1.txt
└─1605
添加字段属性表.xlsx
Dir(“Pathname”) 参数为文件夹路径,路径以 ”\” 结尾,例:Dir("C:\Users\Administrator\Desktop\vba\demo\")。
执行Dir函数返回文件夹下的第一个文件名:01 (1).xlsx
继续执行Dir(不需要加参数),会返回第2、3……序列的文件名,直到遍历结束返回一个空字符串(””)。
在本例中执行Dir函数4次即返回所有文件名,在第5次执行时便返回空字符串并销毁对象,如果继续执行第6次则程序报错。
根据Dir函数返回文件名的逻辑我们可以构造一个循环体来自动遍历文件,取文件名。
使用 Do While(文件名不等于””)……Loop的循环结构正合适。
在Microsoft Windows 中, Dir 支持多字符(*) 和单字符 (?) 的通配符来指定多重文件。
*匹配多字符
匹配出demo文件夹下的3个Excel文件。将Dir的参数稍加改动,使用一个(*)匹配文件名,使用一个(*)匹配扩展名中的sx或者x。
fileName =Dir("C:\Users\Administrator\Desktop\vba\demo\*.xl*")
Sub DirTestLoop_Star()
DimfileName As String
DimrowIndex As Integer
Columns(1).Clear '清空A列,便于观察程序执行结果
fileName = Dir("C:\Users\Administrator\Desktop\vba\demo\*.xl*")
rowIndex = 1
Do While fileName <> ""
Cells(rowIndex,1).Value =fileName
rowIndex =rowIndex + 1
fileName = Dir
Loop
End Sub
返回结果:
?匹配单字符
匹配01(1).xlsx 与 01 (2).xlsx
fileName = Dir("C:\Users\Administrator\Desktop\vba\demo\01(?).xlsx")
返回结果:
实际操作中操作目的往往是获取文件全路径,然后对其进行改名、移动、复制、删除等的操作,那么如何获取文件的全路径呢?很简单,我们已经获取了文件名,在前面加上文件夹路径便是全名了。在代码中一般先声明一个变量folderPath存储文件夹路径,然后加上文件名(fileName)组成文件的全路径(fullPath)。
继续在上一例的基础上稍加改动——fullPath=folderPath &filename
Sub DirTestLoop_QuestionMark_GetFullPath()
DimfolderPath, fileName, fullPath As String
DimrowIndex As Integer
Columns(1).Clear
folderPath = "C:\Users\Administrator\Desktop\vba\demo\"
fileName = Dir(folderPath &"01 (?).xlsx")
rowIndex = 1
Do While fileName <> ""
fullPath =folderPath & fileName
Cells(rowIndex,1).Value =fullPath
rowIndex =rowIndex + 1
fileName = Dir
Loop
End Sub
返回结果:
ThisWorkbook对象的Path 属性与Name、FullName属性
讲VBA的文件路径,了解ThisWorkbook对象的Path属性与Name、FullName属性是必要的。至于什么是ThisWorkbook对象,理解为本工作簿就可以,Path 属性返回工作簿所在的文件夹路径,Name 属性返回工作簿的名称,FullName则返回工作簿的全路径。
※Chr(10)的效果是换行,Chr函数将一个ASCII码转为相应的字符,此处不做展开。
不难看出,使用ThisWorkbook.Path & “\”& ThisWorkbook.Name即可拼接出当前工作簿的全路径,而这种拼接的结果等于ThisWorkbook.FullName的返回值。
实际操作中操作对象文件夹难免有可能会改名、移动,此时代码中写死的文件夹路径便指向了一个错误的或者不存在的路径。需要考虑这种情况的时候ThisWorkbook.Path的好处便凸显了,一般的会将代码所在的Excel工作簿放到操作对象文件夹内,用ThisWorkbook.Path& “\”便取到了folderPath的值。
folderPath = ThisWorkbook.Path &"\"
接上节,在本节的开始我将代码所在的Excel工作表放入了要操作的文件夹,此时文件夹的树形结构如下:
该文件夹的树形结构如下C:\Users\Administrator\Desktop\vba\demo
│ 01 (1).xlsx
│ 01 (2).xlsx
│ VBAdemo.xls
│ 文件夹改名.xls
│ 新建文本文档.txt
├─1601
│ 430Forbidden.png
├─1603
│ 1.txt
└─1605
添加字段属性表.xlsx
在代码中文件夹路径(folderPath)采用ThisWorkbook.Path& "\"的方式来获取;Dir函数的第2参文件属性设置为vbDirectory,设定该参数后Dir函数会返回无属性文件、目录或文件夹名。
用Dir(folderPath,vbDirectory)遍历一下,然后分析结果。
※在上图左侧的结果中“.”、“..”是DOS中用到的虚拟目录,此处不展开。
红色框中是我们要获得的子文件夹,其与另外的目录或者文件的明显辨识符号就是“.”——文件扩展名与目录中都存在.符号。
Sub DirTest_Directory()
DimfolderPath, fileName As String
DimrowIndex As Integer
Columns(1).Clear
folderPath = ThisWorkbook.Path &"\"
fileName = Dir(folderPath,vbDirectory)
rowIndex = 1
Do While fileName <> ""
If InStr(fileName, ".") =0 Then '如果fileName中不包含.则执行If语句块内代码
Cells(rowIndex, 1).Value = fileName
rowIndex = rowIndex + 1
End If
fileName = Dir
Loop
End Sub
※InStr(fileName, ".") = 0并不能做到“万无一失”,因为会有文件夹名字包含.的情况。
做到这样仍不能确保返回的结果一定是文件夹,举一例——万一有人把“新建文本文档.txt”改成了“新建文本文档”,你可以猜得到结果。
为了避免这样的结果,我们可以继续对结果做一个判断,使用GetAttr函数返回文件、文件夹或目录的属性(为一个整型值),然后使其与vbDirectory(是指代“目录或文件夹”的属性值,值为16,与Dir的第2参有所不相同)属性值进行 And 运算,在下式成立的时候输出结果。
GetAttr(folderPath& fileName) And vbDirectory)= vbDirectory
这样if语句块的判断逻辑就是下面的样子了。
If InStr(fileName, ".") = 0 Then '如果fileName中不包含.则执行If语句块内代码
If (GetAttr(folderPath& fileName) AndvbDirectory) = vbDirectory Then
Cells(rowIndex,1).Value =fileName
rowIndex = rowIndex +1
End If
End If
※如果对GetAttr(“文件(夹)全路径”)、And运算的逻辑有兴趣可以查查资料,没兴趣直接抄代码就可以。如果对自己的文件规整程度有信心,这一个判断可以省去。Dir(folderPath, vbDirectory)遍历的结果是目录、文件夹、文件都存在的,我们仍将Dir返回的结果赋值给变量fileName,只是为了写代码的时候方便(也便于阅读)而不去声明太多的变量,如果觉得别扭你可以将该变量声明为name或其他便于自己识别的变量名。
教程的初衷是面向实际操作的,编写中也在尽可能的秉持这一点。
基于这一点我们就不得不考虑“多层文件结构”的情况,如下,案例文件夹被改造成多级:
C:\Users\Administrator\Desktop\vba\demo
├─1601
│ └─3层
├─1603
│ └─3层
│ └─4层
└─1605
└─3层
遍历所有层级文件夹路径的代码如下:
Sub DirTest_GetAllDirectory()
DimfolderName As String
Dim i,k As Integer
Columns(1).Clear
Cells(1, 1).Value = ThisWorkbook.Path &"\"
i = 1
k = 1
Do While i <= k
folderName = Dir(Cells(i,1).Value,vbDirectory)
Do
If InStr(folderName, ".") = 0 And _
(GetAttr(Cells(i, 1).Value & folderName) And vbDirectory) = vbDirectory Then
k = k + 1
Cells(k, 1).Value = Cells(i, 1).Value & folderName & "\"
End If
folderName = Dir
Loop Until folderName = ""
i = i + 1
Loop
End Sub
※空格+下划线+回车 将一行代码换行
先将主路径ThisWorkbook.Path & "\"(父节点)放入单元格Cells(1, 1),进入外层循环Do While i <= k ……Loop 指定要遍历的父节点,内层循环Do……Loop Until folderName = ""遍历子节点存入A列(作为外层循环的父节点),遍历完成后进入外层循环继续获取父节点……如此往复,当i=k时外层循环进入某一层的最后一个“父节点”的遍历,如果该“父节点”没有子目录则循环到此终止。
PS.
Do……Loop Until folderName = "" 效果等同于Do While folderName <> "" ……Loop 可以自行了解一下。
在第1节“遍历文件夹下的文件”中讲述了如何获取一个路径下的文件,在第2节“遍历子文件夹”中讲述了如何获取一个路径下(包括本层)的所有层级目录路径。
获取所有子文件夹路径放入A列,然后逐个遍历取文件夹路径再遍历其下的文件即可实现本节的操作目的。前面两节都是在向工作表存入数据,本节需要从工作表遍历每行获取数据了,由此便会引入for循环。
For i = 1 To 5 Step 1 '如果step(循环步数)为1可省略
循环体
Next i
通过设定行索引为变量可以操作工作表某列单元格的读取或写入。
上图中使用for语句对C列的前8个单元格分别进行赋值,但是循环的终止行索引往往是不固定的,尤其是在读取数据的时候,不可能每次都在代码中将最后一行的索引“手动写入”。获取最后一行的索引值需要使用Rows.Count属性获取本表最大行索引值maxRow,然后使用End(xlUp).Row在某列的maxRow单元格向上找到最后一个非空的单元格取其行号lastRow。
具体代码见下图:
为了使代码看起来简单,遍历分为逐个遍历取文件夹路径再遍历其下的文件两个过程。
1、获取所有文件夹路径到A列
获取所有层级文件夹路径到A列,这个过程直接用2.2节“遍历所有文件夹”的代码即可。
Sub GetAllDirectory()
DimfolderName As String
Dim i,k As Integer
Columns(1).Clear
Cells(1, 1).Value = ThisWorkbook.Path &"\"
i = 1
k = 1
Do While i <= k
folderName = Dir(Cells(i,1).Value,vbDirectory)
Do
If InStr(folderName, ".") = 0 And _
(GetAttr(Cells(i, 1).Value & folderName) And vbDirectory) = vbDirectory Then
k = k + 1
Cells(k, 1).Value = Cells(i, 1).Value & folderName & "\"
End If
folderName = Dir
Loop Until folderName = ""
i = i + 1
Loop
End Sub
2、遍历A列文件夹,取文件
这个过程可以通过改造1.1节“构造循环体实现自动遍历”的代码实现
在该过程的开始调用上节的GetAllDirectory()过程 将所有文件夹路径放入A列, dir函数的路径参数声明为变量folderPath,然后通过for循环在A列取文件夹路径赋值给该变量。内层Do While……Loop循环取出folderPath下的所有文件放在B列。
Sub LoopGetFile()
CallGetAllDirectory '调用GetAllDirectory()过程
Columns(2).Clear
DimfileName, folderPath As String
DimrowIndexA, rowIndexB, maxRow, lastRowA As Integer
maxRow = Rows.Count
lastRowA = Cells(maxRow, 1).End(xlUp).Row
ForrowIndexA = 1 To lastRowA
folderPath =Cells(rowIndexA, 1).Value
fileName = Dir(folderPath)
rowIndexB =Cells(maxRow, 2).End(xlUp).Row+ 1
Do While fileName <> ""
Cells(rowIndexB, 2).Value = folderPath &fileName
rowIndexB = rowIndexB + 1
fileName = Dir
Loop
NextrowIndexA
End Sub
这是番外篇,有兴趣可读。
使用Excel应用的Application.FileDialog属性打开对话框拾取一个文件夹路径,而不用把路径写入代码或者把代码所在工作簿放入文件夹下取Thisworkbook.Path。
定义一个函数GetMainDirectory,用来拾取主文件夹路径。
※VBA自定义函数 Function可以自行扩展,在工作表中使用自定义函数可以弥补许多原生函数的不足。
'定义一个函数,用来拾取主文件夹路径
'传入msoFileDialogFolderPicker(文件夹拾取对话框),返回字符串类型的文件夹路径
Function GetMainDirectory(ByVal DialogType As MsoFileDialogType) As String
WithApplication.FileDialog(DialogType)
If .Show= True Then
GetMainDirectory =.SelectedItems(1)
End If
End With
End Function
下面的代码中调用 GetMainDirectory函数打开Excel自带对话框,拾取文件夹路径赋值给数组索引位置为1的元素,然后遍历出所有子文件夹。
※代码中使用了数组、单元格地址的Range表达方式。如果恰好你是有兴趣阅读到这里,尝试把它与Dir函数2.2“遍历所有文件夹”的代码做下对比。
'获取主文件夹路径及其所有子文件夹路径
'使用 GetMainDirectory 函数获取主文件夹路径,然后遍历其子夹,取路径放入A列
Sub GetFolderList()
Dim folder(1 To 10000)
DimfolderName As String
Dim i,k
folder(1) = GetMainDirectory(msoFileDialogFolderPicker)+ "\"
Range("a1") = folder(1)
i = 1: k = 1
Do While i <= k
folderName = Dir(folder(i),vbDirectory)
Do
If InStr(folderName, ".") = 0 And folderName <> "" Then
k =k + 1
folder(k) = folder(i) &folderName & "\"
Range("a" & k) =folder(k)
End If
folderName = Dir
Loop Until folderName = ""
i = i + 1
Loop
End Sub
联系客服