Excel VBA 编程基础 -- 文件处理(八)
前文我们讨论了二进制文件的读写,今天讨论文件的随机读写。所谓随机读写(random access),是指随机指定记录号然后读写该记录。文件的随机读写,一般是指对二进制文件的读写,而且是对定长记录的二进制文件。对于不定长记录文件,因为记录号与相应记录在文件中的位置不存在直接的对应关系,所以无法进行随机存取。对定长记录二进制文件进行随机读写,所使用的读写语句与二进制文件相同,都是 Get / Put。对于 Get / Put 读写语句,请参阅前文,此不赘述。下面,针对前文创建的二进制文件 hello.dat,我们来看看如何实现随机读取。hello.dat 文件由一系列 Long 型整数组成,所以我们将 Long 型整数看作为一个记录,记录长度为 Len(Long) = 4 个字节,hello.dat 共包含 6 个记录。在展示代码之前,我们先来介绍一下 VBA 中的随机数发生器。VBA 的随机数发生器由 Randomize 语句和 Rnd 函数组成。Randomize 语句用来初始化 Rnd 函数,而 Rnd 函数用来随机地返回一个介于 0 和 1 之间的浮点数,即 0 ≤ Rnd() < 1。Randomize 语句用来初始化 Rnd 函数。其一般格式是:其中,可选参数 number 是 Variant 类型或任何合法的数值表达式,用作为随机数发生器的种子(seed)。如果不指定 number,则 Randomize 使用系统时间作为种子值。Rnd 用来生成一个单精度的伪随机数(pseudo-random number)。注:所谓伪随机数(pseudo-random number),是指随机数是根据公式/算法计算出来的,并非真正意义上的随机数。目前所有编程语言的随机数发生器都是伪随机数发生器,因为都是根据公式或算法计算出来的。其中,可选参数 nunber 是 Single 类型或任何合法的数值表达式。number 和 Rnd 返回值之间的关系如下:- number < 0:则 number 用作为种子,因此每次 Rnd 都返回相同的值,因为每次调用的种子都是一样的。
- number = 0:则 Rnd 返回上一次生成的随机数。
- number >0 或省略:则 Rnd 返回序列中的下一个随机数。
生成区间 [ a, b ] 之内的随机数 n 的计算公式如下:n = Int((b - a + 1) * Rnd + a)有了这些准备,下面来看随机读取 hello.dat 中的数据:首先,声明了一个数组,用来存储记录号。这些记录号用随机数发生器生成,因此是随机的。第一个 For 循环就用来生成随机的记录号。因为 Rnd 生成的随机序列有可能有重复,因此要在生成过程中去除重复,方法就是重复扫描已生成的随机序列,看刚刚生成的随机数是否已经存在,如果已经存在,则重复当前循环(通过 i = i - 1),直到当前的随机数不再已生成的随机序列中出现为止,然后进入下一个循环,生成序列中的下一个随机数。通过 n 的计算公式,我们知道生成的随机序列满足:1 ≤ n ≤ 6。记录号的随机序列生成以后,打开文件用于随机存取,则记录长度 Len = Len(di) = Len(Long) = 4。然后使用一个 For 循环,将文件 hello.dat 中的数据记录使用 Get 语句按随机记录号读出,并赋给 di 变量。接下来打印当前循环变量的值、当前的随机记录号、读出数据的十进制和十六进制表示。为了对比,将前文创建的数据文件 hello.dat 中的值图示如下:对照图2 和图1 右侧窗口中结果,可以看出,文件 hello.dat 中的数据确实是根据记录号随机读出的。这里补充一个前文遗漏的细节:文本文件是以行为单位的,而每行的长度是不定的,Print # 和 Write # 会自动向文件中写入一个回车换行符 Chr(13) + Chr(10) 作为行的分隔标记。二进制文件没有明确的分隔标记,对于随机读写,是以定长记录为单位的;对于顺序读写,则以字节为单位。如果我们放大记录的概念,将字节也包括在内的话,则可以说,对于二进制文件,不管是随机读写,还是顺序读写,都是以定长记录为单位的。仔细研究前文的例和本文的例,可以看出,虽然顺序读写和随机读写都是使用 Get/Put 完成,但参数 recnumber 的含义是不一样的。这点要仔细体会。