数组与字典:一次装很多数据
变量只能装一个值。要装一列数据,就得用这两种"容器"。
学习目标
- 说清数组是"一排带编号的格子",并解释为什么下标从 0 开始
- 读懂并写出数组
[ ]与字典{ },会用变量[下标]取数、改数 - 用
For Each逐个处理数组和字典里的数据 - 会用"在数组尾部添加元素 / 过滤数组数据 / 分割字符串"三条数据处理命令
- 识破"赋值 = 取别名"这个大坑,知道下标越界为什么会报错
- 说出数据表(DataTable)是什么,为第 7 课读 Excel 铺路
预计用时:读课 35 分钟 + 上机 25 分钟 | 前置知识:第 3 课(变量与数据类型)、第 5 课(循环,尤其是 For Each) | 本课性质:语法课(基础语法收官),概念与上机并重,是从"单个值"迈向"一批数据"的转折点
知识地图
前三部分是"认识工具"(第 1–2 课)和"基础语法"(第 3–6 课)。前面的变量只能装一个值;本课教你把一批值装进同一个容器,再用循环批量加工。它是第 7 课 Excel 自动化的地基——读取 Excel 得到的就是这里说的数组和数据表。
一、数组:一排带编号的格子
1.1 从"一个格子"到"一排格子"
第 3 课里,变量像一个格子:Dim 姓名 = "张三",一个名字对应一个值。
可现实里数据常常是一整列——五个月份的天数、一个班的学生名单。总不能起五个变量名 天数1、天数2……那样既啰嗦,又没法用循环批量处理。
数组就是把这些格子并排钉在一起,再从左到右编号。用方括号写出来: [1]
Dim 天数 = [31, 28, 31, 30, 31]
为什么需要数组?有了"一排格子 + 编号",才能做两件单独变量做不到的事:一是用编号(下标)按位置取数,比如"取第 3 个";二是用循环从头到尾扫一遍,代码长度不随数据条数增长。数据有一百条,循环还是那三行。
1.2 下标从 0 开始
头号易错点:数组的下标从 0 开始。第一个元素是 天数[0],不是 天数[1]。上例长度是 5,最大下标是 4。
为什么偏偏从 0 起?可以这样记:下标其实是"从第一个格子出发,往右走了几步"。第一个格子还没走,所以是 0; 第二个格子走了一步,所以是 1。第 N 个元素的下标 = N − 1,最后一个元素的下标 = 长度 − 1。
| 你想要 | 下标 | 取值 |
|---|---|---|
| 第 1 个元素 | 0 | 天数[0] → 31 |
| 第 3 个元素 | 2 | 天数[2] → 31 |
| 最后一个元素 | 长度 − 1 = 4 | 天数[4] → 31 |
1.3 混合类型与二维数组
同一个数组里的元素类型可以混着放,也可以再套一个数组(这就是"二维数组"):
Dim 混合 = [1, "小明", True]
Dim 表格 = [["张三", "语文"], ["李四", "数学"]]
输出调试信息(表格[1][0]) // 李四
二维数组是"数组里装的还是数组",可以想成一张表格:外层是行,里层是列。
取数的口诀是先定行、再定列:表格[1][0] 的第一个 [1] 取第 2 行(从 0 起),第二个 [0] 取这一行的第 1 列,结果是 "李四"。
| 写法 | 行(从 0) | 列(从 0) | 结果 |
|---|---|---|---|
表格[0][0] | 第 1 行 | 第 1 列 | 张三 |
表格[0][1] | 第 1 行 | 第 2 列 | 语文 |
表格[1][0] | 第 2 行 | 第 1 列 | 李四 |
易错点(维数不匹配):数组维数在定义时就定死了。把一维数组当二维用(写成 混合[0][0]),或者反过来,都会出错。定义时是几层方括号,取值时就要写几组下标。这条到第 7 课写 Excel 时尤其致命。
二、字典:按名字取东西
字典像通讯录——用名字而不是编号来取。[1]
Dim 学生 = { "姓名": "张三", "年龄": "26", "城市": "北京" }
输出调试信息(学生["姓名"]) // 张三
学生["年龄"] = "30" // 直接改
学生["性别"] = "男" // 直接加,字典里就多一项
规则:字典的"名字"(键)只能是字符串;"值"可以是任意类型。取元素时,数组用整数下标,字典用字符串键。
为什么用字典?数组靠"第几个"定位,可你常常只记得数据的含义(姓名、年龄),记不住它是第几个。字典把"含义"直接当钥匙:学生["姓名"] 比 学生[0] 好读得多,也不怕中途插入一项导致序号全变。
| 对比项 | 数组 [ ] | 字典 { } |
|---|---|---|
| 用什么取 | 整数下标(从 0 起) | 字符串键 |
| 顺序 | 有固定先后 | 按名字找,不靠顺序 |
| 典型例子 | 一排天数、一列姓名 | 一条记录的各个字段 |
可增可改:给一个已存在的键赋值就是"改",给一个不存在的键赋值就是"加"——两种写法完全一样,都是 字典["键"] = 值。键必须是字符串,所以别写成 学生[姓名](那样会把 姓名 当成变量)。
三、怎么遍历它们:For Each
第 5 课学的遍历循环,正是为这两种容器准备的:[1]
Dim 天数 = [31, 28, 31]
Dim 合计 = 0
For Each 天 In 天数
合计 = 合计 + 天
Next
For Each 键, 值 In 学生 // 字典:同时拿名字和值
输出调试信息(键 & "=" & 值)
Next
遍历数组时,循环变量(上例的 天)每一轮自动换成下一个元素;遍历字典时,For Each 键, 值 形式可以同时拿到"名字"和"值"。
关键优点:你完全不用自己管下标,自然就不会写错"从 0 还是从 1 起",也不会越界。
小技巧:如果确实需要知道"现在是第几个",可以在循环外先放一个计数变量 Dim i = 0,循环体内处理完再 i = i + 1。用 For Each 管数据、用计数器管序号,两件事分开想,不容易乱。
四、在可视化视图里操作数组/字典
命令区里有一整个 数据处理 分类,下面是认证常考的几条:[2]
| 分类 | 命令 | 作用 |
|---|---|---|
| 数组 | 在数组尾部添加元素 | 往数组末尾追加一个元素 |
| 数组 | 过滤数组数据 | 按条件保留或剔除元素 |
| 字符串 | 分割字符串 | 按分隔符把一段文字切成数组 |
在可视化视图里用一条数据处理命令,流程都差不多:
- 在命令区找到分类(数组类命令在
数据处理 ▸ 数组,分割字符串在数据处理 ▸ 字符串)。 - 把它拖到流程里要执行的位置。
- 选中这条命令,在右侧属性区填:要处理的原数组/原文本、条件或要追加的元素、以及"输出到"哪个变量。
- 用
输出调试信息把输出变量打出来,运行一遍,看结果对不对。
说明:每条命令属性区里的字段名称,以你本机命令面板实际显示的为准;本课只讲"要填哪几类东西"以及结果长什么样,不背字段名。
特别好用的一条:分割字符串。把 "zhangsan|lisi|wangwu" 按 | 分割,得到数组 ["zhangsan","lisi","wangwu"]。CSV、日志这类"用符号隔开"的文本,全靠它变成数组再逐条处理。[2]
五、两个必须知道的坑
坑 1:赋值 = 取别名,不是复制
把数组/字典赋给另一个变量,只是给它起了个别名,两者指向同一份数据。改一个,另一个跟着变:[1]
a = [10, 20, 30]
b = a // b 只是 a 的别名,不是副本
b[1] = 99 // 改 b
输出调试信息(a) // a 也变成了 [10, 99, 30]!
为什么会这样?看图就懂了。执行 b = a 时,并没有搬出一排新格子;只是把 a、b 两个名字贴在了同一排格子上:
所以 b[1] = 99 改的是这排格子的第 2 格,a 再看过去当然也变了:
想要真正独立的一份,要用数据处理里的复制相关命令,不能直接赋值。
易错点(别名):基础类型(数字、字符串、布尔)用 = 是真复制,改副本不影响原值;数组和字典是取别名,改副本会连带改原值。考试问"两个变量什么关系",认准"指向同一份数据"这个说法。备份数组/字典,一律走复制命令。
坑 2:下标越界会报错
数组只有 5 个元素却去取 天数[5],运行时会直接报错停下。遍历时用 For Each 就不会踩这个坑,因为它是"有多少取多少"。
易错点(越界):长度 5 的数组,合法下标是 0 到 4,5 就已经在格子外面了。手工写循环 For i = 0 To 5 很容易多走一步——要么写成 To 4,要么干脆用 For Each。
六、预告:数据表——Excel 的接口
官方还有一种特殊容器叫数据表(DataTable),可以理解成"带表头的二维数组",能排序、能筛选:
构建数据表 用一维数组做表头、二维数组做数据。[2]
下一部分读 Excel 时,读出来的通常就是数据表——所以这一课是 Excel 的地基。
承上启下:到这里,基础语法全部学完:变量装单值、数组装一排、字典装一条记录、循环批量加工、条件做判断。下一课把它们接上 Excel——用数组承接读出来的表格数据,用 For Each 逐行处理,用写入命令回填。
Python 写法对照:这一课的写法几乎一样——下标从 0 开始、字典用键取值,两种语言完全一致。 真正的差别只在长度和遍历上。
| 要做的 | BotScript(本课程主线) | Python |
|---|---|---|
| 定义数组 | Dim 天数 = [31, 28, 31] | 天数 = [31, 28, 31](一样) |
| 按下标取元素 | 天数[0] | 天数[0](一样,都从 0 起) |
| 二维数组 | 表格[1][0] | 表格[1][0](一样) |
| 定义字典 | Dim 学生 = { "姓名": "张三" } | 学生 = {"姓名": "张三"}(一样) |
| 取 / 改 / 新增键 | 学生["年龄"] = "30" | 学生["年龄"] = "30"(一样) |
| 遍历数组 | For Each 天 In 天数 | for 天 in 天数: |
| 遍历字典 | For Each 键, 值 In 学生 | for 键, 值 in 学生.items(): |
| 取长度 | 本课没有引入取长度的命令,直接用 For Each 就能避开越界 | len(天数) |
| 复制一份数组 | b = a 只是别名,不是副本 | b = a 也只是别名;要真副本得写 b = list(a) |
最后一行的别名陷阱,两种语言一模一样——本课讲的那个坑(改 b 结果 a 也变了)
在 Python 里同样会发生。所以你学到的不是"BotScript 的怪癖",而是这类数据结构共有的性质:
变量装的是"指向那排格子的地址",不是格子本身。Python 里要真复制,用 list(a)。
本课小结
必记要点
- 数组是"一排带编号的格子",下标从 0 起;长度为 N 时最大下标是 N − 1。
- 数组可混放类型,也可嵌套成二维数组;
表格[行][列],行列都从 0 起。 - 字典按名字取值,键只能是字符串;
字典["键"] = 值既能改、也能加。 - 用
For Each遍历,不必自己管下标,也避免越界。 - 数据处理三件套:在数组尾部添加元素、过滤数组数据、分割字符串(文本 → 数组)。
- 数组/字典赋值是取别名,不是复制;要独立副本必须用复制命令。
- 数据表 = 带表头的二维数组,是第 7 课 Excel 读写的数据形态。
自测
不看上面,凭记忆作答。答错不要紧,解析会告诉你为什么。
上机实践(回到 Windows)
任务 A(求总和):定义数组 [31,28,31,30,31],用 For Each 求总和(应为 151),输出结果。
任务 B(字典):定义字典 {"姓名":"张三","年龄":"26"},输出"姓名"对应的值;再往里加一个键"性别",输出整个字典,观察它长了什么样。
任务 C(文本转数组):用 分割字符串 命令,把 "a,b,c,d" 按 , 切分,输出得到的数组,确认它是 ["a","b","c","d"]。
任务 D(验证坑 1):照上面"别名陷阱"那段代码跑一遍,确认 a 真的被改掉了。把这个现象发我,说说你打算怎么避免它。
任务 E(故意制造报错):先定义 Dim 天数 = [31, 28, 31](长度 3),然后故意输出 天数[3]。运行,把报错原文抄给我;再把同一段逻辑改成 For Each 遍历重跑,确认不再报错。
任何一步卡住,把命令属性截图 + 报错原文发给我。
精读一手来源
本节首选:《开发者指南(第二辑)· 数据获取和处理》的"数组""数据表"两节——它给了每条命令的属性配置和运行结果。 语法层面看 《BotScript 语言参考 · 复合类型》。 语法速查见 参考页:BotScript 语法速查(已补入数组/字典一节)。