VBA-TG第2节|数组/字符串与文件
最近更新:'2019-05-02'
1.数组声明和使用,LBound与UBound函数
2.Split函数,动态数组
3.Open/Close/Line Input/EOF/Print
4.文件编号规则,DIR函数用法
主要内容介绍:
1.使用数组的方法,以及为什么会有“下标越界”错误?
2.动态数组,它与Split怎样拆分字符串?
3.在VBA中读取和写入文本文件
4.一次读入文件夹中全部文件
1.数组声明和使用,LBound与UBound函数
1.1数组简单的介绍
可以用房子做比喻,普通变量相当于是单独的一栋房子;而数组是栋很大的房子,住了很多业主.


案例1:数字转换为中文的大写
将等级中数字转换为中文的大写.

Sub ChineseNumber()
Dim i, k
Dim cn(10) As String
cn(0) = "零": cn(1) = "壹": cn(2) = "贰": cn(3) = "叁": cn(4) = "肆":
cn(5) = "伍": cn(6) = "陆": cn(7) = "柒": cn(8) = "捌": cn(9) = "玖":
For i = 4 To 20
Cells(i, 4) = cn(Cells(i, 4))
Next i
End Sub
案例2:统计40位同学的班费

1.1.1数组的下标范围

1.1.2下标不能越界
案例1
一个不存在的下标,就会报错

案例2

1.1.3下标越界的处理方法

Sub UBoundDemo()
Dim a(2 To 7) As Date
MsgBox (UBound(a))
End Sub
Sub LBoundDemo()
Dim a(2 To 7) As Date
MsgBox (LBound(a))
End Sub

2.Split函数,动态数组
2.1使用数组的原因
- 对于大批量数据的计算使用数组的效率显著高于直接操作单元格。
- 出于语法要求,很多情境下必须使用数组。
以下案例是通过拆分单元格,如何作为一个一个变量返回结果?方法就是通过数组.


2.2案例分析:split拆分
案例:将B3的名单进行拆分,并将拆分后的名单放入到D列

方案1
Sub SplitDemo()
Dim a() As String, k As Long, i As Long
a = Split(Cells(3, 2), ",")
k = 3
For i = LBound(a) To UBound(a)
Cells(k, 4) = a(i)
k = k + 1
Next i
End Sub
代码最终显示的结果如下:

方案2
Sub SplitDemo()
Dim a() As String, i As Long, x
a = Split(Cells(3, 2), ",")
i = 3
For Each x In a
Cells(i, 4) = x
i = i + 1
Next x
End Sub
代码最终显示的结果如下:

注意:使用 For each遍历数组时,循环变量必须为变体类型!


2.3redim语句:修改数组长度
redim语句可随时多次重新修改数组长度.


案例1:去除非空字符串的数量,知道真实的数组下标
怎么去除非空字符串的数量,以此知道真实的数组下标

3.Open/Close/Line Input/EOF/Print
3.1用VBA程序直接读取文本文件
用VBA程序直接读取文本文件,用VBA程序直接只需要4步!
- 打开文本文件
找到指定文件井调入内存 - 读取一行内容
将每行视作一个字符串 - 是否已到末尾?
如果是末尾就不再读取 - 关闭文本文件
保存文件,请空内存
3.1.1打开文本文件的总流程

3.1.2打开文本文件
找到指定文件并调入内存,所使用的语句是open语句

3.1.3读取一行内容
将每行视作一个字符串
line input知识点
- line input #1,s
从1号文件读取一行文本,把它作为字符串保持到s这个变量中. - Line Input是顺序读写模式
每执行一次 Line Input,就自动向下读取新的一行,不能向回读取已经读过的内容。


3.1.4是否已到末尾?
如果是末尾就不再读取
EOF(n):相关知识点
- EOF(n):判断代号n的文件是否到达文件末尾。
-
EOF(1)=False等价于Not EOF(1)
3.1.5关闭文本文件

关闭文件,减少内存.
3.1.6整个案例代码
将"客户信息.txt"这个文件涉及到北京的信息写入到excel中
Sub readtext()
Dim s As String, i As Long
Open "C:\Users\Administrator\Desktop\11\客户信息.txt" For Input As #1
i = 1
Do While EOF(1) = False
Line Input #1, s
If Left(s, 2) = "北京" Then
Cells(i, 1) = s
i = i + 1
End If
Loop
End Sub
3.2用VBA程序直接写入文本文件
用VBA程序直接,写入文本文件只要3步:
(1)打开文件
(2)写入一行
(3)关闭文件





案例分析
将在同一个工作簿的3个worsheet表单(部门1,部门2,部门3)汇总在一起.

Sub writeText()
Dim i As Long, w As Worksheet
Open "d:\部门汇总.txt" For Output As #1
For Each w In Worksheets
i = 3
Do While Trim(w.Cells(i, 2)) <> ""
Print #1, w.Cells(i, 2); "--"; w.Cells(i, 3)
i = i + 1
Loop
Next w
Close #1
End Sub
代码显示的结果如下:

4.文件编号规则,DIR函数用法
每打开一个文件的时候需要指派一个唯一的数字编号.

特别是打开多个文件的时候,需要将每个文件指派为多个数字编号.
4.1文件编号规则
4.1.1:文件编号案例
案例:将"残本1"和"残本2"读入到excel,并保存到第三个文件中.
具体要求如下图:

这时候文件需要同时打开两个文件,而且都是input的方式进行读入.这两个文件读取的时候指派为#1和#2,以免相互冲突.
相关代码如下:
方法1
Sub readTest13()
Dim s As String, i As Long
Open "C:\Users\Administrator\Desktop\d\demo\残本1.txt" For Input As #1
Open "C:\Users\Administrator\Desktop\d\demo\残本2.txt" For Input As #2
i = 2
Do While EOF(1) = False
Line Input #1, s
Cells(i, 2) = s
i = i + 1
Loop
Close #1
Do While EOF(2) = False
Line Input #2, s
Cells(i, 2) = s
i = i + 1
Loop
Close #2
Open "C:\Users\Administrator\Desktop\d\demo\残本4.txt" For Output As #3
i = 2
Do While Trim(Cells(i, 2)) <> ""
Print #3, Cells(i, 2)
i = i + 1
Loop
Close #3
End Sub
方法2
Sub readTest13()
Dim s As String, i As Long
Open "C:\Users\Administrator\Desktop\d\demo\残本1.txt" For Input As #1
Open "C:\Users\Administrator\Desktop\d\demo\残本2.txt" For Input As #2
i = 2
Do While Not EOF(1) Or Not EOF(2)
If Not EOF(1) Then
Line Input #1, s
Cells(i, 2) = s
i = i + 1
End If
If Not EOF(2) Then
Line Input #2, s
Cells(i, 2) = s
i = i + 1
End If
Loop
Close #1: Close #2
Open "C:\Users\Administrator\Desktop\d\demo\残本4.txt" For Output As #3
i = 2
Do While Trim(Cells(i, 2)) <> ""
Print #3, Cells(i, 2)
i = i + 1
Loop
Close #3
End Sub
注意事项:
output as #03可以改成#01
原因是input as #01已经关闭,此时内存中已经不再有其他编号为1的文件.
4.1.2文件编号规则总结:
- 正在打开的文件,必须有唯一编号
- 文件关闭后,编号释放并可以重用
4.2DIR函数用法:显示同一目录下所有文件的名字
dir函数显示同一目录下所有文件的名字

案例1:dir函数的运用1
Sub dirTest13()
Dim f As String
f = Dir("C:\Users\Administrator\Desktop\d\demo\")
MsgBox f
End Sub
代码最终显示的结果如下:
显示的是该目录下第一个文件的名字

显示的是该目录下第二个文件的名字,又需要怎么做呢?
Sub dirTest13()
Dim f As String
f = Dir("C:\Users\Administrator\Desktop\d\demo\")
f = Dir
MsgBox f
End Sub
代码显示的最终结果为

显示的是该目录下第三个文件的名字,又需要怎么做呢?
Sub dirTest13()
Dim f As String
f = Dir("C:\Users\Administrator\Desktop\d\demo\")
f = Dir
f = Dir
MsgBox f
End Sub
代码显示的最终结果为

f = Dir再运行,又得到什么结果呢?
Sub dirTest13()
Dim f As String
f = Dir("C:\Users\Administrator\Desktop\d\demo\")
f = Dir
f = Dir
f = Dir
MsgBox f
End Sub

运行结果是空字符串.因为文件总共有3个文件,没有找到新的文件名,只好返回空字符串.
因此当Dir返回空字符串时,代表所有文件名都已经被找到,本次查找结束。
4.2.1dir函数使用注意事项
1.地址后面还要加反斜杠"\"结尾代表文件夹,否则会被当作一个文件.

2.Dir函数查找文件的顺序在不同系统中各不相同,往往与我们看到的不一致.

3.Dir函数第二次以上运行时,不要写参数

4.Dir函数返回空字符串时,代表所有文件已被找到

4.2.2使用dir()函数一次性读取所有文件的常用方法

为了让程序更加容易理解,将程序写成了两个单独的主程序和子程序.子程序的功能是将任何一个交个它的文件名用open打开,然后读入到一个新建的工作表中.而主程序主要起扫描文件的作用.而得到一个文件名就交给子程序,由子程序专门负责读取处理.
这里需要注意的是dir函数返回的是文件自己的名字.而不能告诉我们具体在那个文件目录下.

而我们的open语句必须知道所有的路径名称,才能打开这个文件.

因此我们不能用dir返回的字符串直接交给open语句.因此需要连结上完整的路径名称.

再把完整的路径名称交给子过程,交给open语句才能实现完整的打开操作.

这是初学者容易犯错的地方.特别需要注意.
知识延伸:InStrRev函数
返回一个字符串在另一个字符串中出现的位置,从字符串的末尾算起。
InstrRev(stringcheck, stringmatch[, start[, compare]])
InstrRev 函数语法有如下命名参数:
- stringcheck 必需的。要执行搜索的字符串表达式。
- stringmatch 必需的。要搜索的字符串表达式。
- start可选的。数值表达式,设置每次搜索的开始位置。如果忽略,则表示从字符串末尾位置开始搜索。
- compare可选的。数字值,指出在判断子字符串时所使用的比较方法。如果忽略,则执行二进制比较。compare参数值如下:
常数 | 值 | 描述 |
---|---|---|
vbUseCompareOption | –1 | 用Option Compare语句的设置值来执行比较。 |
vbBinaryCompare | 0 | 执行二进制比较。区分大小写。 |
vbTextCompare | 1 | 执行文字比较。不区分大小写。 |
vbDatabaseCompare | 2 | 只用于Microsoft Access。基于您的数据库信息执行比较。 |
Sub readFromFile(fullName As String)
Dim ws As Worksheet, i As Long, s As String
Set ws = Worksheets.Add
ws.Name = Mid(fullName, InStrRev(fullName, "\") + 1)
Open fullName For Input As #1
i = 1
Do While Not EOF(1)
Line Input #1, s
ws.Cells(i, 3) = Mid(s, InStr(s, "电话") + 3, 8)
i = i + 1
Loop
Close #1
End Sub
4.3dir打开文件夹目录的文件类型

4.3.1 dir打开同一文件下的excel文件(文件夹包含excel文件)
那么如果想把在一个文件夹下,成千上万的工作簿文件打开,也可以使用这样的方法
我们可以得到文件的完整路径名称,再用open语句打开.

4.3.2 dir只打开同一文件下的excel文件(文件夹包含excel和txt文件)
有两种方法.首先来看一下方法1
方法1

方法2


4.3.3 dir只打开同一文件下的指定类型的文件(文件夹包含多种格式的文件)

4.4dir打开路径和全名的作用
之前有提到过dir打开文件夹,就可以知道所在目录的名称,如下截图的内容:

那么dir打开路径和全名,会有什么结果呢?结果返回的就是所在文件的名称


如果dir打开路径和全名,而这个文件根本不存在,那又有什么结果呢?结果返回的是空字符串.

因此可以得到如下结果:

这一点在我们读取文件的时候非常重要,可以在dir打开路径和名称的时候,先判断dir返回的结果不等于空字符串.而这个文件存在的情况下,我们采用open语句打开.

4.5dir打开文件夹的子文件夹的一层文件
dir打开的是文件目录下的文件,而子文件夹的文件是不会处理的.


如果需要打开子文件夹的文件,需要在dir函数增加vbdirectory,dir就会打开文件夹所有的文件名以及子文件夹中的一层文件名.


4.5dir打开文件夹的所有的文件以及子文件下的所有深层次文件
这时候就需要用到递归算法.
