Excel VBA 里,除了 For…Next,另外两种循环我也建议你早点搞明白
很多人学 VBA,前面学到 For...Next 的时候,会有一种“哦,循环我会了”的感觉。
这个阶段其实挺正常的。因为 For...Next 的确是最容易上手的那一个,按行扫、按列扫、固定跑几次,都很好理解。
但真开始写点能落地的小程序之后,问题马上就来了。
你会发现,表格里的很多事,根本不是“从第几行到第几行”这么简单。有时候你只是想把某一列里的单元格都过一遍;有时候你压根不知道要循环多少次,只能先跑着,碰到空行再停,或者等用户输入对了再停。
这时候,如果脑子里只有 For...Next,写起来就会有点拧巴。
所以这篇我想接着前一篇往下写,把另外两种常用循环也讲一下:
顺手再带一下几个你以后迟早会碰到的循环控制语句。不是为了把语法背全,而是为了以后你看到这些代码,别发懵;真到自己写的时候,也知道大概该选哪个。
一、先别急着背语法,先把这三种循环分开
我自己现在会把常见循环粗暴地分成三类,够用了。
第一种,For...Next。这个上一篇已经讲过了,适合那种次数一开始就知道的情况。比如从第 2 行处理到第 20 行,或者从第 3 列扫到第 8 列。
第二种,For Each...Next。这个更像是:别管第几个了,你把这里面的每一个,都拿出来处理一遍。
第三种,Do...Loop。这个是:我也不知道要跑几次,先跑着,条件不满足了再停。
你把这三句话记住,后面很多事就顺了。真不用一上来就和语法死磕。
二、For Each...Next,适合那种“挨个看一遍”的活
For Each...Next`` 第一次看会有点怪,因为它不像 For i = 1 To 10` 那样直观。
它最常见的写法是这样:
ForEach 元素 In 集合 语句Next 元素
“集合”这个词听上去有点书面,其实放到 Excel 里,一点都不玄乎。
你完全可以把它理解成一堆东西,比如:
也就是说,For Each...Next 更像是在说:
把这一堆东西一个个拿出来,统一处理。
它跟 For...Next 最大的区别就在这儿。For...Next 更在意“第几个”;For Each...Next 更在意“每一个”。
三、拿个表格来看,这种场景就很适合 For Each
比如你现在有一张客户跟进表,D 列是跟进状态。你想把所有“未联系”的单元格标出来,不然每天看表的时候很容易漏。
表格可以像这样:
这种时候,当然也能用 For...Next 去写。但说实话,我平时写这类东西,更愿意直接上 For Each...Next。因为我根本不关心这是第 3 行还是第 5 行,我只想把 D2 到 D6 这一串状态,一个个检查过去。
代码可以这样写:
Sub MarkStatus()Dim cell As RangeForEach cell In Range("D2:D6")If cell.Value = "未联系"Then cell.Interior.ColorIndex = 6EndIfNext cellEndSub
这段代码读起来其实很顺:
这里的 cell 不是行号,也不是列号,它就是“当前这个格子”。这一点一旦转过来,For Each...Next 就不难了。
四、什么时候我会优先用 For Each
这个问题其实比语法本身更重要。
我自己的习惯很简单。
如果我更关心的是位置,比如第 2 行、第 3 行、第 4 行,那我一般还是会用 For...Next,因为它天然就是按编号走的。
但如果我关心的是“这一堆东西都过一遍”,那 For Each...Next 会更顺手。
比如:
这种时候,用 For Each 写出来,真的比 For...Next 更像人话。
五、再看一个例子:遍历所有工作表
这个也是很典型的 For Each...Next 用法。
有时候别人发你一个工作簿,里面工作表很多,表名还乱七八糟,前后带着空格。你想统一清一下。
这种时候就很适合这么写:
Sub TrimSheetName()Dim ws As WorksheetForEach ws In Worksheets ws.Name = Trim(ws.Name)Next wsEndSub
如果你把这段代码翻成大白话,其实就是一句:
把这个工作簿里的每张表都拿出来,把表名前后的空格去掉。
所以你会发现,For Each...Next 最大的优点,不是语法多高级,而是它很贴合人脑对“遍历”的理解。很多“所有……都……”这种需求,用它写就是顺。
六、Do...Loop 就是另一回事了,它适合“先干着再说”
前面那个 For Each...Next,解决的是“一个个处理”的问题。
Do...Loop 不一样。它处理的是这种情况:
我现在根本不知道要重复几次。
这种场景在 Excel 里太常见了。
比如:
这些事有个共同点:开始的时候,你没法先写死“循环 8 次”还是“循环 20 次”。那 For...Next 就不太合适了,这时候 Do...Loop 就会顺很多。
七、最常见的用法:往下扫,碰到空行就停
比如有一张订单表,A 列是订单号。你的需求很简单:
只要 A 列还有值,就继续处理;要是哪一行 A 列空了,就停。
表格可以这样放:
这种表,我平时就很容易直接写成 Do While:
Sub CheckOrder()Dim i AsInteger i = 2DoWhile Cells(i, 1).Value <> ""If Cells(i, 4).Value = "未审核"Then Cells(i, 4).Value = "待复核"EndIf i = i + 1LoopEndSub
这段代码其实不复杂。它的意思就是:从第 2 行开始,只要 A 列没空,就继续往下处理。处理完一行,i 加 1,再去看下一行。
这里最关键的,不是“跑多少次”,而是“条件是不是还成立”。
只要条件成立,循环就继续。条件不成立,循环自己就结束。
这就是 Do...Loop 最常见、也最实用的一类写法。
八、Do While 和 Do Until,其实是两种说法
这一对很多人刚开始会绕进去。
但说实话,没必要把它搞得太复杂。你就记这两句:
比如下面两句,本质上表达的是一回事:
DoWhile Cells(i, 1).Value <> ""
和
DoUntil Cells(i, 1).Value = ""
前一句是在说:只要不是空行,就继续。后一句是在说:直到遇到空行,才停。
一个偏正着说,一个偏反着说。实际写的时候,你用自己读起来更顺的那个就行。
我自己很多时候更习惯 Do While,因为更直接一点。但这不是硬规则,看个人。
九、还有一种写法,很多人第一次会漏掉:先执行,再判断
Do...Loop 还有一种挺常见的形式,是把条件放在后面。
像这样:
或者这样:
它和前面最大的区别只有一个:
它会先跑一次,然后才判断要不要继续。
这个差别听上去小,实际挺关键。
因为有些场景下,你就是想让程序无论如何先执行一下。比如输入框这种事,至少要先弹一次,后面你才有资格去判断“输入得对不对”。
所以这一块,你可以直接这么记:
够用了。先别给自己绕太深。
十、循环里还有两个特别常用的小东西:Exit For 和 Exit Do
这个我觉得比很多老语法都更值得先记。
它们的作用很干脆,就是:
提前跳出循环。
比如你在一张很长的订单表里找订单号,一旦找到了,就没必要继续往后翻了。这时候就可以直接退出。
拿 Exit For 来说,像这样:
Sub FindOrder()Dim i AsIntegerFor i = 2To100If Cells(i, 1).Value = "DD003"Then MsgBox "找到了"ExitForEndIfNext iEndSub
这段代码很实在:找到就停,不浪费后面的循环。
Do...Loop 里也是一样的道理,可以用 Exit Do。这种写法实际工作里很常见,尤其是查找、匹配、命中后停止这类需求。
十一、剩下那几个,现阶段知道就行,别太用力学
学循环的时候,大家还会碰到几个名字,看着挺唬人,其实你现阶段不用在它们身上花太多时间。
1)While...Wend
这个是比较老的循环写法,长这样:
它当然也能用,但现在写 VBA 的时候,一般不会把它当主力。因为 Do...Loop 更灵活,也更常见。
所以你知道它是个老写法就够了。以后真在旧代码里碰到,别不认识就行。
2)GoTo
GoTo 的作用,就是直接跳到某个标记位置继续执行。
VBA 里是支持的。但说实话,这个东西我不太建议在日常代码里乱用。因为一旦跳来跳去,代码特别容易变得很乱。写的时候你可能觉得方便,过两天自己回来改,往往先把自己看晕。
3)Gosub...Return
这个也是老语法,意思大概是先跳到某一段去执行,执行完了再回来。
现在已经不算主流写法了。因为真要拆逻辑,用 Sub 和 Function 会更清楚。
所以这类语句,你知道它们存在、知道大概干什么,就已经够了。没必要一开始就往里钻。
十二、真到自己写代码的时候,怎么选循环,其实没有那么玄
说到底,循环这块最容易让人乱的,不是语法本身,而是老觉得“怎么这么多种”。
其实你把场景分开,就没那么复杂了。
已知次数的,用 For...Next。一堆对象逐个处理的,用 For Each...Next。次数不知道、只能看条件什么时候停的,用 Do...Loop。
差不多就是这么回事。
很多时候,代码写不顺,不是你不会语法,而是你一开始就选错了那种循环。这个感受,我自己学的时候还挺明显的。明明知道好几种写法,但一旦场景没对上,写出来就会很别扭。
十三、最后收个尾
如果你前一篇已经把 For...Next 看懂了,那这一篇其实是在帮你把循环这件事补完整一点。
For...Next 适合次数明确的事。For Each...Next 适合把一堆单元格、一堆工作表、一堆对象挨个处理。Do...Loop 则更像是“先做,什么时候停看条件”。
你把这几种思路分开,后面很多 VBA 小程序,脑子里就不会只剩下一种写法了。
而且说到底,Excel 里大量自动化需求,难点通常都不是“算法有多复杂”,而是它太机械、太重复。人手一条一条做会烦,也容易错。循环之所以重要,就是因为它特别适合接这类活。
这也是我一直觉得,VBA 里循环这块值得反复看的原因。不是因为它花哨,恰恰相反,是因为它太常用了。