万普插件库

jQuery插件大全与特效教程

Excel常用技能分享与探讨(5-宏与VBA简介 VBA的数组)

在VBA(Visual Basic for Applications)中,数组是一种非常有用的数据结构,用于存储一系列相同类型的元素。数组可以是一维的,也可以是二维或多维的。使用数组可以有效地处理批量数据,提高代码的效率和可读性。


1. 什么是数组?

生活化比喻

想象你有一排储物柜(数组),每个柜子(数组元素)有一个编号(索引),柜子里可以存放物品(数据)。
所有柜子的类型相同(比如只能放书),或者类型不同(混合放书、衣服等)。

编程概念

  • 数组:一组连续内存空间存储的多个数据,通过索引快速访问。
  • 核心特点
    避免重复定义变量(如num1, num2, num3...)
    高效处理批量数据(如Excel表格中的一列数据)

2. 数组的声明与初始化

2.1 静态数组:固定大小的储物柜

' 语法:Dim 数组名(索引范围) As 数据类型
Dim locker(0 To 2) As String  ' 3个柜子,索引0、1、2
locker(0) = "书包"            ' 第一个柜子放书包
locker(1) = "水杯"            ' 第二个柜子放水杯
locker(2) = "笔记本"          ' 第三个柜子放笔记本

关键细节

  • 默认索引从0开始,Dim arr(2)实际是0,1,2共3个元素!
  • 可用Option Base 1强制索引从1开始(写在模块顶部)

2.2 动态数组:可伸缩的储物柜

Dim locker() As String   ' 声明时不指定大小
ReDim locker(2)          ' 初始化3个柜子(索引0-2)
ReDim Preserve locker(4) ' 扩容到5个柜子,保留原有内容

那以上代码中ReDim Preserve是什么?为什么动态数组要用到它?

2.2.1. ReDim Preserve 是什么?

想象你有一个 可伸缩的储物柜(动态数组),原本有5个柜子,现在想扩容到10个柜子,但希望保留之前柜子里的物品
ReDim Preserve 就是用来做这件事的:调整数组大小的同时,保留原有数据

2.2.2. 核心作用

  • 调整数组大小:扩大或缩小数组的容量。
  • 保留原数据:原有索引位置的数据不会丢失。
  • 适用场景:当你无法提前预知数组需要多大,需要动态扩展时使用(比如读取不确定行数的数据)。

2.2.3. 基础语法

ReDim Preserve 数组名(新的大小)

示例代码

Dim students() As String  ' 声明动态数组
ReDim students(2)         ' 初始大小:3个元素(索引0-2)
students(0) = "小明"
students(1) = "小红"
students(2) = "小刚"

' 扩容到5个元素(索引0-4),且保留原有数据
ReDim Preserve students(4)
students(3) = "小李"  ' 新增第4个元素
students(4) = "小张"  ' 新增第5个元素

2.2.4. 关键细节与限制

(1) 只能调整最后一维的大小

如果你想找到“最后一维”的概念,比如在多维数组中,通常我们谈论的是数组的最高维度(即最外层的维度)。对于二维数组来说,你可以认为“最后一维”实际上是“最内层的维度”,即每一行的元素。在二维数组中,每一行可以被视为一个单独的“一维数组”。

  • 一维数组:自由调整。
  • 多维数组:只能调整最后一个维度(比如二维数组的列数)。
    错误示例(尝试调整第一维):
Dim matrix(2, 3) As Integer ' 3行4列 
ReDim Preserve matrix(5, 3) '  报错:不能调整第一维(非最后一维)
ReDim Preserve matrix(2, 5) '  合法:调整列数(最后(右)一维) 

(2) 缩小数组会丢失超出部分的数据

ReDim Preserve students(1)  ' 缩小到2个元素(0-1)
' 原students(2)的值"小刚"将永久丢失!

(3) 索引范围变化

调整后的数组索引从LBound到新指定的上限,例如:

  • 原数组:ReDim students(2) → 索引0-2(3个元素)
  • 调整后:ReDim Preserve students(4) → 索引0-4(5个元素)

2.2.5. 为什么需要Preserve?

对比 ReDim 和 ReDim Preserve 的区别:

操作

是否保留数据

典型用途

ReDim arr(5)

清空所有数据

初始化数组或彻底重置

ReDim Preserve arr(5)

保留原数据

动态扩容/缩容且不丢失数据

2.2.6. 性能警告!

  • 频繁使用ReDim Preserve会降低效率(尤其是处理超大数组时)。
  • 优化建议
  • 尽量预估初始大小,减少调整次数。
  • 如果必须频繁调整,可每次扩大一定容量(比如每次扩容50%)。

2.2.7.总结

  • ReDim Preserve = 扩容/缩容 + 保留数据
  • 牢记限制:只能动最后一维,多维数组要小心!
  • 慎用场景:处理海量数据时避免频繁调用。

通过这个关键字,你可以像玩“拉伸储物柜”一样灵活管理数组,同时保证数据安全不丢失!

2.3. LBound 和 UBound:获取数组边界

作用

  • LBound(数组名, 维度):返回数组指定维度的最小索引
  • UBound(数组名, 维度):返回数组指定维度的最大索引

示例

Dim arr(3 To 8) As Integer  ' 索引3-8(共6个元素)
Debug.Print LBound(arr)     ' 输出3
Debug.Print UBound(arr)     ' 输出8

Dim matrix(2, 5) As String ' 二维数组
Debug.Print UBound(matrix, 1) ' 第1维最大索引:2
Debug.Print UBound(matrix, 2) ' 第2维最大索引:5

注意

  • 省略维度参数时,默认检查第1维
  • 安全遍历数组必用此函数(避免越界错误)

2.4. Erase:释放数组内存

作用

  • 对动态数组:彻底清空并释放内存(数组变为空)
  • 对静态数组:重置所有元素为默认值(如数值变0,字符串变空)

示例

Dim arr() As Integer
ReDim arr(5)
Erase arr  ' arr变为空数组,不可直接访问

注意

  • 清空后需重新ReDim才能使用

2.5. Option Base 1:改变默认索引起点

作用

强制数组索引从 1 开始(默认从 0 开始)。

用法

Option Base 1  ' 写在模块顶部(所有数组受影响)
Dim arr(5) As Integer ' 索引1-5(共5个元素)

注意

  • 需在模块最顶部声明
  • 与显式指定索引冲突时以显式声明为准
    (如 Dim arr(0 To 5) 不受 Option Base 1 影响)

总结表格

关键字/函数

作用

适用场景

Dim

声明数组初始大小

定义新数组

ReDim

调整动态数组大小

动态扩容/缩容

Preserve

保留数据调整大小

需保留原数据的动态调整

LBound/UBound

获取数组边界

安全遍历数组

Erase

清空数组内存

重置或释放数组

Option Base

改变索引起点

需要索引从1开始时使用


3. 数组的赋值与访问

3.1 直接赋值

' 一维数组
Dim fruits(2) As String
fruits(0) = "苹果"
fruits(1) = "香蕉"
fruits(2) = "橘子"

' 二维数组(想象成Excel表格)
Dim table(1 To 3, 1 To 2) As String ' 3行2列
table(1, 1) = "姓名"
table(1, 2) = "年龄"
table(2, 1) = "小明"
table(2, 2) = "25"

3.2 快速初始化:Array函数

Dim colors() As Variant
colors = Array("红", "绿", "蓝") ' 索引从0开始

3.3 从Excel范围读取数组(超高效!)

Dim dataRange As Range
Set dataRange = Range("A1:C10")    ' 选中A1到C10区域
Dim arrData As Variant
arrData = dataRange.Value          ' 直接存入二维数组

4. 遍历数组:逐个访问储物柜

4.1 一维数组遍历

For i = LBound(arr) To UBound(arr)
    Debug.Print "第" & i & "个元素:" & arr(i)
Next i

4.2 二维数组遍历(类似Excel行列)

For row = 1 To 3
    For col = 1 To 2
        Debug.Print "第" & row & "行,第" & col & "列:" & table(row, col)
    Next col
Next row

函数解释

  • LBound(arr):返回数组最小索引
  • UBound(arr):返回数组最大索引
  • UBound(arr, 2):获取二维数组的列数(第二维)

5. 动态数组的实用技巧

5.1 清空数组

Erase locker  ' 动态数组内存释放,静态数组重置为初始值

5.2 动态扩容保留数据

ReDim Preserve locker(10) ' 从原来的0-2扩容到0-10

注意:扩容时原有数据保留,但只能调整最后一维


6. 常用数组函数

6.1 Split:字符串转数组

Dim text As String
text = "苹果,香蕉,橘子"
Dim fruitArr() As String
fruitArr = Split(text, ",") ' 按逗号分割为数组

6.2 Join:数组转字符串

Dim combinedText As String
combinedText = Join(fruitArr, "|") ' 输出 "苹果|香蕉|橘子"

7. 避坑指南:常见错误

错误1:下标越界

Dim arr(2) As Integer
arr(3) = 10 ' 错误!索引最大是2

正确做法

If i >= LBound(arr) And i <= UBound(arr) Then
    arr(i) = 10
End If

错误2:类型不匹配

Dim nums(2) As Integer
nums(0) = "ABC" ' 错误!不能将字符串存入Integer数组

正确做法
使用Variant类型数组存储混合数据:

Dim mixedArr() As Variant
mixedArr = Array(1, "苹果", True)

ReDim 不能用于静态数组

Dim staticArr(5) As Integer 
ReDim staticArr(10) '  报错!静态数组大小固定

多维数组调整限制

Dim matrix(2, 3) As Integer 
ReDim Preserve matrix(5, 3) '  调整行数(第一维) 
ReDim Preserve matrix(2, 5) '  报错!只能调整最后一维(列数)

索引起点混淆

Option Base 1
Dim arr(5) As Integer ' 索引1-5(共5个元素)
Debug.Print arr(0)    '  报错!索引从1开始

8. 实战案例:用数组快速处理Excel数据

场景:计算A列数值的总和(避免逐个操作单元格)

Sub SumColumn()
    ' 1. 读取数据到数组
    Dim dataRange As Range
    Set dataRange = Range("A1:A10000") ' 假设有1万行数据
    Dim arrData As Variant
    arrData = dataRange.Value ' 存入二维数组(索引从1开始!)
    
    ' 2. 遍历数组计算总和
    Dim total As Double
    For i = 1 To UBound(arrData, 1) ' 行数
        total = total + arrData(i, 1) ' 第一列
    Next i
    
    ' 3. 输出结果
    MsgBox "总和:" & total
End Sub

效率对比

  • 直接操作单元格:约3秒(1万行)
  • 使用数组:约0.01秒!

案例2:快速转置行列

Sub 行列转置()
    Dim 原始数据(1 To 3, 1 To 2) As Variant
    原始数据 = Range("A1:B3").Value
    
    Dim 转置结果(1 To 2, 1 To 3) As Variant
    For i = 1 To 3
        For j = 1 To 2
            转置结果(j, i) = 原始数据(i, j)
        Next
    Next
    
    Range("D1:F2").Value = 转置结果
End Sub

案例3:多表数据汇总

Sub 合并数据()
    Dim 总数据() As Variant
    Dim 行号 As Long: 行号 = 0
    
    For Each ws In Worksheets
        Dim 当前表数据 As Variant
        当前表数据 = ws.Range("A1").CurrentRegion.Value
        
        ReDim Preserve 总数据(行号 To 行号 + UBound(当前表数据) - 1, 1 To 5)
        For i = 1 To UBound(当前表数据)
            行号 = 行号 + 1
            For j = 1 To 5
                总数据(行号, j) = 当前表数据(i, j)
            Next
        Next
    Next
    
    Sheets("总表").Range("A1").Resize(行号, 5).Value = 总数据
End Sub

9. 练习与巩固

练习1:反转数组

将数组 [1,2,3,4,5] 反转为 [5,4,3,2,1]
提示:用循环交换首尾元素。

练习2:统计字符出现次数

将字符串 "hello world" 存入数组,统计字母l出现的次数。


总结

  • 数组核心优势:提升代码速度,简化数据管理
  • 学习路径:静态数组 → 动态数组 → 多维数组 → 实战应用
  • 避坑口诀
    ① 索引从0还是1?用LBound和UBound检查!
    ② 动态数组先ReDim,扩容记得加Preserve!

通过以上步骤,即使零基础也能逐步掌握VBA数组的核心用法!建议边学边写代码测试,遇到问题随时回看文档


下章预告:《VBA之字典》

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言