前面两篇讨论的数组都是一维的,VBA 中的数组类型还可以是多维的,最多可以到 60 维。我们今天讨论二维数组,有了二维数组的基础,更高维的数组也就容易理解了。我们前面在处理如图1所示的数据集时,是先定义一个 UDT,然后再声明一个元素类型为所定义 UDT 的数组。这是一维数组的处理方式。现在我们有了二维数组的概念,就来看看如何使用二维数组处理这个数据集。我们说过,数组就是一组元素的集合,这组元素有着同样的数据类型,譬如一维数组的例子中的 MathScore。在图1所示的数据集中,各列的数据类型并不相同,如何作为相同类型的元素统一在数组中呢?我们知道,VBA 中有一个可以容纳各种基本类型的变体类型 Variant,我们就以这个类型作为二维数组的元素类型。代码如下:Dim MScore2D(9, 4) As VariantDim row As IntegerDim col As IntegerFor row = 2 to 11For col = 1 to 5MScore2D(row-2, col-1) = Cells(row, col).ValueNext colNext row
首先,声明一个二维数组 MScore2D,这个数组第一维的上界是 9,第二维的上界是 4,这个声明等价于(假设 Option Base 0,以后不再说明):Dim MScore2D(0 to 9, 0 to 4) As Variant
所以,MScore2D 是一个 10 x 5 的二维矩阵。读取 Excel 数据集使用了双重循环:外层是行循环,内层是列循环。我们上面所声明的 MScore2D 是一个固定大小的静态数组,使用我们前面讨论过的动态数组的概念读取整个数据集的代码如图2所示:关于使用 CountA 方法获取数据集行数的说明,请参考前一篇文章。这里要说明的是,声明多维动态数组,和声明一维动态数组一样,都使用同样的 Dim MyArray() As DataType,只是在使用 Redim 重新定义数组大小时,需要给出各维的上界,譬如 ReDim MScore2D(size - 2, 4)。接下来,使用双重循环读取整个 Excel 数据集。只不过这里的外层循环使用 Do While ... Loop 结构,以单元格的值是否为空来判断循环何时结束。内层循环,因为列数已知,所以使用 For ... Next 结构。不过,这里因为整个数据集的行数已经通过 CountA 获知,所以外层循环也可以使用 For ... Next 结构。我们可以把上面的循环代码重写如下:For row = 2 to sizeFor col = 1 to 5MScore2D(row - 2, col - 1) = Cells(row, col)Next colNext row
注意:因为 Cells 对象的默认属性就是 Value,所以 Cells(row, col) 就等于 Cells(row, col).Value。上面读取 Excel 数据集的方法并不是最优的,使用双重循环读取每一个单元格效率很低。读取 Excel 数据集的最优化方法如图3 所示:首先,获取数据集的行数,然后构造区域的边界字符串 rBoundary,接着用 rBoundary 去界定要存取的区域 Range(rBoundary),最后取该区域的值直接赋给动态数组 MScore2D。要注意的是,Range(rBoundary).Value 返回一个二维数组(赋给变量 MScore2D),这个二维数组每一维的下界都是 1,而且这个下界并不受 Option Base 设置的影响。这或许是因为 Excel 工作表的行与列都是从 1 开始的缘故吧。最后,LBound 和 UBound 两函数返回数组特定维的下界和上界:- LBound(MyArray) ,等同于 LBound(MyArray, 1),返回第一维的下界
- UBound(MyArray),等同于 UBound(MyArray, 1),返回第一维的上界
- LBound(MyArray, n):返回第 n 维的下界,如果 n 超出了 MyArray 的维数范围,将导致“下标越界”的错误。
- UBound(MyArray, n):返回第 n 维的上界,如果 n 超出了 MyArray 的位数范围,将导致“下标越界”的错误。