使用 Dir 函数遍历文件和目录
ivbscript
Vb 编程中,可以通过使用控件 DriveListBox 、DirListBox 和 FileListBox 的组合,来得到
某个目录下的文件或文件夹,继而可以通过递归调用来得到磁盘上所有的文件或文件夹,但
是,如果可以仅通过一个函数就能完成以上的工作,何乐而不为?
第一部分:Dir 函数
语法:Dir[(pathname[, attributes])]
作用:返回一个 String,用以表示一个文件名、目录名或文件夹名称。
参数说明:
pathname:字符串,要查找的文件所在目录名称;
attributes:常数,指定了文件的属性。数值和描述见下:
常数 值 描述
vbNormal 0 (缺省) 没有属性的文件
VbReadOnly 1 只读文件
vbHidden 2 隐藏文件
VbSystem 4 系统文件
vbVolume 8 卷标文件
vbDirectory 16 无属性文件及其路径和文件夹
在使用中,我们可以使用通配符来制定要查找的文件,比如:
dir("c:\*.txt") 返回 C 盘下后缀名为”txt”的文件;
dir("c:\?") 返回 C 盘下后缀名为”txt”,文件名四个字符且包含”est”的文件。
关键点:1、要得到其它匹配 pathname 的文件名,可再次调用 Dir,且不要使用参数;
2、如果已没有合乎条件的文件,则 Dir 会返回一个零长度字符串 ("");
3、Dir 返回的文件或文件名是无序的。
第二部分:递归调用
从第一部分我们可以看出,如果我们先使用一个带有 Pathname 参数的 Dir 函数,然后不
断使用不带参数的 Dir 函数,那么我们就可以获得查找目录下的所有的文件或文件夹。
比如
FileName = Dir(“C:\”, vbDirectory)
If FileName <> "" Then
Do While FileName <> "" ‘获得的文件名不是空字符串的话继续执行
Dosomething ‘这里是调用处理过程
FileName = Dir ‘调用无参 Dir 函数
Loop
End If
第三部分:简单图形界面
在程序中,不仅仅是后台程序,更重要的还有图形界面。
这里使用 StatusBar 控件、ImageList 控件、TreeView 控件和 ListView 控件将写入文本中
保存的目录和文件显示出来。
由于以上几个控件不是默认的控件,要使用这几个控件,应该使用菜单“工程\部件”,然
后在“控件”选项卡中选择“Microsoft Windows Common Controls ”,单击“确定”,这四个控件
就被加入到了工具箱中。
我们在 FormMain 中放置一个 StatusBar 控件、两个 ImageList 控件、一个 TreeView 控件
和一个 ListView 控件,具体放置如下图所示:
ImageList1 用于 TreeView1 中显示图片,图像的大小为 16×16,图像主要用到了以下几个:
ImageList2 用于 ListView1 中显示图片,图像大小为 32×32,图像用到了以下几个:
设置 TreeView1 控件的图像列表为 ImageList1;设置 ListView1 控件的普通图像列表为
ImageList2,小图标列表为 ImageList1。
(一)StatusBar 控件的简单使用
在 StatusBar1 控件上点击鼠标右键,选择“属性”。在“通用”选项卡中选择“样式”为“0 -
sbrNormal”(多窗格样式)。在“窗格”选项卡中,通过“插入窗格”共建立了四个窗格,其中索
TreeView1
ListView1
StatusBar1
ImageList1 和
ImageList2
引 1、3 号窗格的文本分别为:“包含目录”、“包含文件”;2、4 号窗格的文本为空,在编程的
时候可以这样设定某个窗格的文本:
(Index).Text = “这里是设定的文本” ‘Index 对应设定的索引号
(二)TreeView 控件的简单使用
在 TreeView1 控件中添加一个节点的方法如下:
(relative, relationship, key, text, image, selectedimage)
用法可参看 Msdn :《Add 方法(Nodes 集合)》一节。在程序中可以这样使用:
Dim nodX As Node
‘声明 Node 变量。
Set nodX = (, , "r", "Root",1,2)
‘第一个节点,文本为”Root”,”r”是这个节点的标识字符串,
‘1 是在关联的 ImageList 控件中的图像的索引,2 是在 Node 被选中时显示的图像索引。
Set nodX = ("r", tvwChild, "child1", "Child",3,4)
‘这个节点是节点 1 ("Root") 的子节点,其中”r”是关联节点的标识字符串,
‘tvwChild 是此节点和节点 1 之间的关联方式(子节点),
‘文本为”Child”,”child1”是这个节点的标识字符串
另外,我们在程序中还要用到 Treeview 控件中 Node 对象的 Tag 属性,它可以帮助该节
点记录额外的数据:
= “额外数据”
(三)ListView 控件的简单使用
设置 ListView1 控件的外观:
=Value
‘Value 有四个值,默认是 lvwIcon 0(图标),即显示为图标和文本标签模式。
在 ListView1 控件中添加对象的方法如下:
(index, key, text, icon, smallIcon)
程序中可以这样使用:
Dim ItmX As ListItem
Set ItmX = (, , “显示文本”, 1, 2)
第四部分:文件读写操作
文件的写操作:
Open Filename For Output As #1
Print #1, “写入的数据”
Close #1
文件的读操作:
Open Filename For Input As #2
Line Input #2, SaveLine
Close #2
另外需要重点提及 Seek 函数和 Seek 语句:
Seek 函数可以返回文件当前读写的位置:Seekpos = Seek(filenumber)
Seek 语句可以设置文件当前读写的位置:Seek #filenumber, SeekPos
第五部分:主要程序分析
程序包含一个模块(Model1)和一个窗体(FormMain)。
在程序中采用如下方式存放文件:
<S>文件夹全路径
<D>文件夹名
<F>文件名
主程序以及说明如下:
Model1 部分:
Public Declare Function timeGetTime Lib "" () As Long
‘返回从 Windows 开始启动经过的毫秒数
Sub main()
‘显示主窗体
End Sub
Public Sub CreateList(DirPath As String) ‘创建文件列表到文件
Dim FileName As String
Dim DirnamePath() As String ‘记录目录的全路径
Dim DirName() As String ‘记录目录的名称
Dim DirCount As Integer ‘也就是 DirnamePath()、DirName() 中的个数
Dim i As Integer
DirCount = 0
ReDim DirName(DirCount)
ReDim DirnamePath(DirCount)
'If Right(DirPath, 1) = "\" Then DirPath = Left(DirPath, Len(DirPath) - 1)
‘开始本来有这个防止传入不带”\”的路径,后来改在函数调用的之前处理这个问题
Print #1, "<S>" & DirPath
‘<S>表示目录全路径;<D>表示目录名称;<F>表示文件名称
‘其中,文件名称和目录名称不要求全路径
DirPath = DirPath & "\"
'如果给出的路径不带"\",那么就加上,但在实际过程中不用判断,直接加上
FileName = Dir(DirPath, vbDirectory)
‘这里调用 Dir 函数获得第一个匹配的文件或目录名。
‘需要注意的是:Dir 每次返回的文件或目录名并不是按照某个次序返回
‘参数 vbDirectory:指定无属性文件及其路径和文件夹。
‘想得到其它匹配的文件或目录名,再一次调用无参 Dir()
If FileName <> "" Then
Do While FileName <> ""
If Left(FileName, 1) <> "." Then ‘防止”.”或者”..”
If (GetAttr(DirPath & FileName) And vbDirectory) <> 16 Then
'如果不是文件夹的话
Print #1, "<F>" & FileName
Else
If DirName(0) <> "" Then '如果 DirName(0)已经使用
DirCount = DirCount + 1 ‘使数组下标加一
ReDim Preserve DirName(DirCount)
ReDim Preserve DirnamePath(DirCount)
End If
DirName(DirCount) = "<D>" & FileName
DirnamePath(DirCount) = DirPath & FileName
End If
End If
FileName = Dir ‘调用无参 Dir 函数
Loop
End If
If DirName(0) <> "" Then '如果 dirName(0)中有信息,那么就执行以下语句
For i = 0 To DirCount ‘循环 1
Print #1, DirName(i) ‘这里是将得到的目录写入文件,与循环 2 分开
Next
For i = 0 To DirCount ‘循环 2
Call CreateList(DirnamePath(i)) ‘递归调用 CreateList 函数
Next
End If
' For i = 0 To DirCount ‘这里是循环 1 和循环 2 的结合,造成的错误是
' Print #1, DirName(i) ‘写入一个目录,然后调用递归函数,
' Call CreateList(DirName(i)) ‘结果得到的文件内容与我们期待的不同
' Next
End Sub
FormMain 部分:
Private Sub MnuCreate_Click() ‘创建文件
'Dim atime1 As Long ‘返回从 Windows 开始启动经过的毫秒数
‘Dim atime2 As Long ‘返回从 Windows 开始启动经过的毫秒数
‘atime = timeGetTime ‘这里是我用来测试写入文件所用时间
Open & "\" For Output As #1 ‘这里假设是存在程序目录下的
Call CreateList("c:")
'注意,这里传递不带"\"的参数,在 CreateList 中将会处理”\”。
Close #1
‘atime2 = timeGetTime
' MsgBox (atime2 - atime1) ‘显示写入文件耗时,这里实际耗时 769ms,
‘以我的 C 盘为例,包含 23330 个文件;1721 个文件夹
‘其中未包含系统以及隐藏文件或文件夹
‘生成文件大小 266,429 字节
MsgBox "create file ok" ‘提示创建文件成功
End Sub
Private Sub MnuFileRead_Click()
Dim ReadLine As String ‘ 从文件中读入一行的数据存放在此
Dim ReadHead As String
Dim ReadMid As String
Dim ReadEnd As String
Dim ReadPos As Integer
Dim NodX As Node
Dim Seekpos As Long
Open "D:\vbsavenew\file\" For Input As #2 ‘打开文件
Line Input #2, ReadLine ‘读入文件第一行,这里是最初的目录路径
Set NodX = ( ,tvwFirst, Mid(ReadLine, 4, Len(ReadLine) - 3) & "\",
Mid(ReadLine, 4, Len(ReadLine) - 3), 4, 5)
‘
Seekpos = Seek(2)
'定位目录全路径在文件中的字节位置
‘这里将给我们在 ListView1 中显示文件和目录提供方便
= Seekpos '把 Seekpos 记录到 Tag ,方便以后调用
Do Until EOF(2) ‘读入文件直到文件结尾
Line Input #2, ReadLine ‘仍然是按每一行的方式读入
Seekpos = Seek(2) ‘记录位置
ReadHead = Left(ReadLine, 3) ‘
Select Case ReadHead
Case "<S>" ‘如果是<S>的话才做处理
ReadPos = InStrRev(ReadLine, "\")
ReadMid = Mid(ReadLine, 4, ReadPos - 3)
ReadEnd = Right(ReadLine, Len(ReadLine) - ReadPos)
Set NodX = (ReadMid, tvwChild, ReadMid & ReadEnd
& "\", ReadEnd, 4, 5)
‘
= Seekpos '把 Seekpos 记录到 Tag ,方便以后调用
Case "<D>" ‘不做处理
Case "<F>" ‘不做处理
End Select
Loop
Close #2 ‘关闭文件
End Sub
Private Sub TreeView1_NodeClick(ByVal Node As )
‘当 TreeView1 上的某个节点被鼠标点击时
Dim SeekFile As Long ‘文件读取位置
Dim ReadLine1 As String
Dim ReadHead1 As String
Dim GetFD As String '获得文件或目录名
Dim ItmX As ListItem
Dim DirCount As Long ‘记录目录数目
Dim FileCount As Long ‘记录文件数目
‘清除 ListView1 中的原有项目
SeekFile = ‘这里就要用到 中记录的位置了
If = True Then = False Else = True
= ‘设置窗体的 Caption 为点击节点的全路径
Open & "\" For Input As #3 ‘打开文件
Seek #3, SeekFile ‘指定文件读取位置
Do Until EOF(3) ‘读入文件直到文件结尾
Line Input #3, ReadLine1 ‘读入一行数据
ReadHead1 = Left(ReadLine1, 3) ‘获得标记
GetFD = Right(ReadLine1, Len(ReadLine1) - 3)
Select Case ReadHead1
Case "<S>" ‘如果是全路径
Close #3 ‘关闭打开的文件
(2).Text = DirCount ‘显示文件夹数目
(4).Text = FileCount ‘显示文件数目
Exit Sub
Case "<D>" '如果是目录
Set ItmX = (, , GetFD, 1) ‘显示目录
DirCount = DirCount + 1 ‘文件夹数目计数加一
Case "<F>" '如果是文件
Set ItmX = (, , GetFD, 2) ‘显示文件
FileCount = FileCount + 1 ‘文件数目计数加一
End Select
Loop
Close #3 ‘关闭文件
(2).Text = DirCount ‘显示文件夹数目
(4).Text = FileCount ‘显示文件数目
End Sub
第六部分:补充和建议
程序在 WinXp+sp1, 环境编译通过。程序完成后运行时的界面如图:
(一) 这个程序的界面和功能还有待完善。读者可以尝试 1、建立整个磁盘的文件树,
2、获取文件和文件夹的属性以及大小;
(二) 如果这个程序只是针对单机,那么反而效率低,但是如果是远程控制,而且需要
传输的次数小的情况下……这就看你的了;
(三) 在写程序过程中一定要养成良好的书写习惯最好是每个变量都要声明,可以选上
Vb 菜单中的“工具/选项/编辑器”中的“要求变量声明”一项。