李懿 Excel学习 2016-07-05
微信扫一扫
关注该公众号
复合文档的解析基础
终于要开始写程序了,不过在正式解析Excel文件之前,我们先来学习一点预备知识。
3.1 自定义数据类型
在VBA中,有一个自定义的数据类型,在其内部可以包含几个不同的类型的数据。其声明的语法如下:
[Private | Public] Type varname
elementname [([subscripts])] As type
[elementname [([subscripts])] As type]
. . .
End Type
举个简单的例子:
Type MyType
ValA As Long
ValB As String
ValC As Byte
ValD As Double
ValE As Integer
End Type
上述示例声明了一个名为MyType的自定义类型的数据,其中包含了ValA,ValB等5个元素的数据。MyType可以直接作为一种数据类型使用,并且可以每个元素都可以单独进行访问。
图 13 自定义类型的使用
其元素的使用和一般变量的使用方法一致。
使用自定义数据类型的好处就是,在变量内部的各个元素的存储位置都是连续的,这样就可以很方便地获取复合文档中的各个内容。通过VarPtr函数可以获取变量的地址。我们来测试一下以下代码:
Sub Test()
Dim MyTypeV As MyType
Debug.Print VarPtr(MyTypeV.ValA)
Debug.Print VarPtr(MyTypeV.ValB)
Debug.Print VarPtr(MyTypeV.ValC)
Debug.Print VarPtr(MyTypeV.ValD)
Debug.Print VarPtr(MyTypeV.ValE)
End Sub
查看立即窗口的输出
图 14 自定义类型各个元素的地址示例
ValA是一个4字节的Long类型的数据,所以第二个元素ValB留下了预留的4个字节的存储空间,ValB的地址恰好比ValA的地址大4个字节。对其他字段验证的结果也是如此。
3.2 VBA中与文件读取的相关方法
首先我们来介绍一下使用到的几个方法:
-
Open方法。该方法用于打开文件,打开后可以得到一个特殊的编号,之后再读取文件的数据都需要该编号。
-
FreeFile函数。
-
Get方法。使用Open方法打开文件后,该方法可以按字节读取数据。
-
Seek方法。使用Open方法打开文件后,该方法可以用于定位指定的文件位置(即地址)。
3.3 数据存储
我们的Excel的数据存储,使用了一种叫做Little Endian的方式,即低位优先的方式。
我们知道,在计算机中的数据存储单位是字节,一个字节是一个8为的二进制数据,存储数据的范围是2的8次方,也就是0-255的无符号数字。为了方便,一般使用2个十六进制数据表示一个字节。比如十进制的255可以用十六进制数FF表示,十进制的32可以用十六进制数20表示。
为了方便比对,我使用了一个名为Binary Editor的二进制编辑软件。使用该软件打开我们的测试文件。
图 15 文件数据示例
这张图里面的所有的数据都是16进制的,最左侧的000000和000010表示这一行数据的起始地址,每一行相差16个字节。最上方的0~F表示偏移量,同一行每一列中相差一个字节。中间的数据为每个字节存放的数据。
我们来看第二行3E那个数据,它处在000010的行,在+8列下,表示这个地址的真实地址是000018。(注意:中间的那个-不是减号或者负号,只是表示8个字节的分割,方便大家看而已)
我们看最开始的两个字节的数据D0和CF,如果把这两个字节的数据按顺序来表示,可以组合成一个十六进制数D0CF,它表示了一个有符号(即有正负的)数-12081,或者一个无符号(即只有正数)数53455。这样的存储方式称为Big Endian(BE)。
然而,Excel中是以Little Endian(LE)存储的。它和BE的方式正好相反,是一个倒序的存储。最开始的两位字节表示的是CFD0,也就是-12336。恰好,我们VBA读取也是LE的方式的,因而完全不用担心会搞错。
3.3 数据读取
下面我们就来验证一下数据,创建一个文件,[Alt+F11]打开VBE,添加一个模块,录入以下代码:
Sub OpenFileTesting()
Dim FS As Integer 'File No.
Dim Val1 As Integer
Dim Val2(1) As Byte
Dim Val3(2) As Integer
FS = FreeFile '获取一个文件流
'打开文件
Open ThisWorkbook.Path & "\test.xls" For Binary Access Read As FS
'顺序读取数据至3个变量
Get FS, , Val1
Get FS, , Val2
Get FS, , Val3
Close FS '关闭文件流
Stop
End Sub
然后打开“本地窗口”
图 16 VBA读取文件结果
为了方便起见,我把这些数字都换成了十六进制。然后和最初我们用BZ读取的文件数据来对比一下。
图 17 BZ与VBA数据读取对比
可以发现:
1、VBA每读取一次数据,再次读取时是会接着当前位置读取下一个数据的。
2、VBA读取的字节数按照变量类型所占用的字节数读取,比如Integer读取两个字节,Byte读取一个字节。
3、每个变量在解析值的时候都是按照LE的规则。
4、数组按照文件顺序读取数据顺序存储给数组的各个元素。
然后我们利用自定义类型再来试试,新建一个模块,录入以下代码并运行:
Type MyFileType
Val1 As Integer
Val2(1) As Byte
Val3(2) As Integer
End Type
Sub OpenFileTesting2()
Dim FS As Integer 'File No.
Dim Val As MyFileType
FS = FreeFile '获取一个文件流
'打开文件
Open ThisWorkbook.Path & "\test.xls" For Binary Access Read As FS
'读取数据至自定义类型
Get FS, , Val
Close FS '关闭文件流
Stop
End Sub
打开“本地窗口”查看一下:
图 18 读取文件至自定义数据类型
看到了什么?是不是和之前分别读取一样的效果?
3.4 小结
到这里为止,我们已经掌握了解析文件所需要的基本编程知识。接下去,让我们一起开始解析一下文件吧。