首页 / 资讯中心 / 文章详情

Excel VBA常用代码实战:对象模型、Range与加载宏避坑

Excel VBA常用代码实战:对象模型、Range与加载宏避坑 ★ FEATURED ARTICLE
简介《EXCEL_VBA常用代码实战大全》是一份面向Excel VBA入门与进阶学习者的代码技巧汇编适合希望用宏自动化重复操作、提升表格处理效率的办公与数据分析人员。压缩包内含1个docx文档大小约15.12MB内容以章节化方式组织便于按知识点检索。已有2071人学习下载说明其在VBA自学群体中具有一定参考价值。文档围绕Range与Cells对象、单元格区域选定、末行末列定位、Find查找替换、选择性粘贴、数组与集合、错误处理及用户窗体等常用主题展开配有技巧编号、目录结构和代码解析可帮助读者理解对象引用方式、常用语句组合与典型应用场景。整体更像一份可随时翻阅的VBA常用代码手册适合边练边查、逐步积累。1. 从一张三十个工作表的台账说起这套 VBA 技巧到底解决什么问题接手一张用了五年的台账最怕什么不是数据量大是点开之后三十个工作表、命名全靠手气每天早上还要重复一遍筛选、复制、粘成新表、另存发送。这种活干到第三周人自然会琢磨用 VBA 把流程固定下来。这份《EXCEL_VBA常用代码实战大全》正是对着这个场景写的它不铺垫语法直接按 Excel 对象模型分成 Range、Worksheet、Workbook、Shape/Chart、Application、对话框六章把 70 个常用技巧逐条落到能粘贴就运行的代码上。适合已经会写公式、想跨过 ExcelVBA编程入门那道坎的人也适合写了几年脚本、想回头补对象模型细节的老手当手册翻。2. 对象模型与目录映射六章 70 个技巧该按什么顺序啃拿到一份几十页的技巧汇编最容易犯的错是从第一页开始顺着读。这类资料的组织逻辑不是教学顺序是对象模型的层级顺序顺着读会在「技巧 30」卡住因为那里已经在用第 20 个技巧里的遍历写法了。先把骨架搭起来再挑需要的段落查效率差好几倍。2.1 三层对象模型Workbook → Worksheet → Range 的引用链VBA 操作 Excel 的一切动作都落在一条从大到小的引用链上Application 是 Excel 本体往下是 Workbooks 集合里的某一本工作簿再往下一层是 Worksheets 集合里的某张表最末端才是 Range 单元格区域。这条链子写全了很长所以 VBA 允许省略前缀代价是省略之后指向谁由「当前状态」决定。Sub ShowChain() 写全的引用链任何时候都指向同一本书、同一张表、同一个格子 Application.Workbooks(台账.xlsm).Worksheets(明细).Range(A1).Value 写全了 省略前缀Range 默认指活动工作表Workbooks 默认指活动工作簿 Range(A1).Value 省略了 活动表不是「明细」时这行就写错地方了 End Sub上面两行代码在只有一张表的时候表现完全一样一旦活动表被用户点到别处第二行就会把数据写到错误的表上而且不报错。这是新手最常遇到、也最难自己发现的错误因为它不抛异常只是静默写到了防不胜防的地方。我一般的做法是凡是跨表、跨簿的操作一律先Dim ws As Worksheet再Set ws ThisWorkbook.Worksheets(明细)后面所有 Range 都挂在这个ws上。ThisWorkbook指代码所在的那本书ActiveWorkbook指当前激活的那本两者在加载宏场景下经常不是同一本这个区别后面第 4 章还会再提。至于Selection、ActiveCell这类对象只在确实要响应用户操作的场景下用纯计算流程里一律不用因为它们的状态你控制不住。2.2 把 70 个技巧映射成六个模块这份资料的目录结构本身就是一张能力地图。把章节和实际场景对照起来看就知道自己该从哪一章补章核心对象典型技巧什么时候会翻到它第 1 章Range引用方式、最后一个非空单元格、Find、合并单元格、Target 参数90% 的日常脚本都在这一章里第 2 章Worksheet引用与遍历、增删表、深度隐藏、删除空行重复行、保护做多表汇总、做模板分发第 3 章Workbook新建打开、判断是否已打开、关闭不弹框、跨簿取数做月度报表合并、做数据抽取第 4 章Shape/Chart插入图形图片、遍历图形、自动生成与导出图表做看板、做图表批量生成第 5 章Application版本与用户名、定时器、屏蔽组合键、隐藏主窗口做工具壳、做加载宏第 6 章对话框内置对话框、文件夹选择器做交互入口阅读顺序建议按使用频率来不是按目录顺序。Range 一章必须吃透因为其余五章的动作最终都要落到单元格上Worksheet 和 Workbook 两章是把它扩展到多表多簿Shape/Chart 和 Application 属于锦上添花等前面跑顺了再看。对话框那一章代码量最小用到的时候现查就行。2.3 上手前的环境准备三个必改的设置代码没错但运行结果诡异八成是环境问题。动手之前先花十分钟把这几项配好能省掉后面大量排查时间。第一文件必须存成.xlsm或.xlsb。存成.xlsx时 VBA 代码会被直接丢弃关闭再打开就没了而且 Excel 不会给你任何强提醒。这不是小概率事件我见过不止一个人写了两天的脚本因为一次「另存为」蒸发。第二把常放脚本的文件夹设为受信任位置文件 → 选项 → 信任中心 → 信任中心设置 → 受信任位置这样打开时不会每次都在顶部弹一条黄色安全警告条。没配的话宏默认被禁用代码一动不动。第三在 VBE 里打开「要求变量声明」。具体操作是 VBE 菜单 工具 → 选项 → 编辑器 → 勾选「要求变量声明」之后新建的每个模块顶部会自动加上Option Explicit。Option Explicit 强制声明变量拼错变量名会直接报编译错误而不是静默得到空值 Sub CheckEnv() Debug.Print Excel 版本: Application.Version Debug.Print 当前用户: Application.UserName Debug.Print 本书路径: ThisWorkbook.Path Debug.Print 本书名称: ThisWorkbook.Name End SubDebug.Print的结果输出到 VBE 的「立即窗口」快捷键 CtrlG这是排查时最轻量的观察手段比弹 MsgBox 打断流程好用得多。上面这段代码同时演示了第 5 章 Application 对象里最常用的三个属性把它当成新环境的第一段验证代码写下来路径、名称符合预期说明文件位置和环境都对。提示把常用的排查语句整理成一个Sub Env()放在个人宏工作簿里换机器复制过去就能用。3. Range 核心拆解引用、定位、查找、合并单元格的落地写法Range 是这份资料里篇幅最大的一章二十个技巧全围着「怎么准确地指到那块格子」和「指到之后怎么改」这两件事转。下面按引用方式、边界定位、条件查找、事件与合并四个场景拆开讲每段都能直接粘进模块跑。3.1 六种引用方式与 Offset、Resize、Union 的组合用法引用一块区域的写法有六种各自的适用场景不一样。写死地址适合固定模板Cells 适合循环里的行列号方括号快捷记号适合调试时临时用Offset 和 Resize 适合在已知锚点的基础上推出周边区域Union 用来把几块不相邻的区域当成一个对象批量处理。Sub RefDemo() Dim ws As Worksheet Set ws ThisWorkbook.Worksheets(明细) ws.Range(A1).Value 直接地址 1 地址字符串最直观 ws.Cells(2, 1).Value Cells 行列 2 行列号配合循环最方便 ws.[A3].Value 方括号 3 快捷记号等价 Range(A3) ws.Range(ws.Cells(1, 1), ws.Cells(5, 3)).Interior.Color RGB(255, 240, 200) 4 两点定区域 ws.Range(B2).Offset(1, 2).Value Offset 5 以 B2 为基准下移 1 行右移 2 列 → D3 ws.Range(B2).Resize(3, 2).Borders.LineStyle xlContinuous 6 从 B2 扩成 3 行 2 列 → B2:C4 End SubOffset(行偏移, 列偏移)的两个参数可以为负Offset(-1, 0)就是上一行负数越界会直接报错循环里用之前一定要判断行号。Resize(行数, 列数)改的是区域大小起点不变Resize(1, 1)就是把它缩回单个格子。真正让人少写循环的是 Union 和 UsedRangeSub AreaDemo() Dim u As Range Set u Union(Range(A1:B2), Range(D5:E6)) 两块不相邻区域合成一个对象 u.Interior.Color vbYellow 一次赋值两块同时上色 Debug.Print u.Areas.Count 2Areas 才是真实块数 Debug.Print ActiveSheet.UsedRange.Address 已使用区域含仅有格式的空单元格 Debug.Print Range(A1).CurrentRegion.Address 由空行空列围起来的连续块 End Sub这里有两个容易搞混的点。Union出来的对象是一个 Range但它的Count统计的是单元格总数要数「几块」得用Areas.Count。UsedRange会把只设过格式、没有内容的单元格也算进去所以拿它来确定数据边界经常偏大处理完数据后手动删掉多余行再保存UsedRange才会缩回来CurrentRegion则是从锚点往外扩到第一个空行空列为止适合读取规整的二维表前提是表中间不能有整行整列的空隙。3.2 找最后一个非空单元格三种写法与各自的坑只要是做动态区域就一定要取边界。资料里给了好几种找最后一行的方法实际用下来差别很大。最省事的是 End(xlUp)Function LastRowEnd(ByVal ws As Worksheet, ByVal col As Long) As Long LastRowEnd ws.Cells(ws.Rows.Count, col).End(xlUp).Row 从最底下一格往上顶 End Function这行代码的逻辑是从该列的最后一个单元格第 1048576 行按 Ctrl↑ 的效果往上跳落到第一个非空单元格。它的坑有两个整列全空时返回 1不是 0调用方要自己判断Cells(1, col)是不是真的有值中间有大段空行时也可能跳过头因为它只认「连续的块」。更稳的是 Find用通配符*从下往上搜Function LastRowFind(ByVal ws As Worksheet, ByVal col As Long) As Long Dim r As Range Set r ws.Columns(col).Find(What:*, _ After:ws.Cells(1, col), _ LookIn:xlFormulas, _ LookAt:xlPart, _ SearchOrder:xlByRows, _ SearchDirection:xlPrevious) If r Is Nothing Then LastRowFind 0 Else LastRowFind r.Row End Function参数含义值得逐个数一遍LookIn:xlFormulas让公式单元格也参与匹配哪怕它算出来是空字符串SearchDirection:xlPrevious表示从后往前搜第一个命中的就是最后一行After:ws.Cells(1, col)是起始位置Find 是从起点之后开始找的起点如果给成默认值搜索区域的第一格就永远搜不到这是无数人「明明有值却返回 Nothing」的根源。第三种是SpecialCells(xlCellTypeLastCell)它取的是表格里最后被用过的单元格包含了格式残留实际位置往往比真实数据大只适合粗判。日常我基本只用 Find 版本稳定且不受筛选、分类汇总的干扰。3.3 Find 与 Like精确查找、模糊匹配、批量替换Find只返回第一个命中结果要拿全部命中必须配FindNext循环而且得自己记下起始地址做终止判断否则会绕圈跑到死循环。Sub FindAll() Dim ws As Worksheet, rng As Range, hit As Range, firstAddr As String, n As Long Set ws ThisWorkbook.Worksheets(明细) Set rng ws.Range(A2:A5000) Set hit rng.Find(What:华东, LookIn:xlValues, LookAt:xlPart, MatchCase:False) If hit Is Nothing Then MsgBox 没有命中 Exit Sub End If firstAddr hit.Address 记住第一个命中用来判断是否绕回 Do n n 1 hit.Offset(0, 6).Value 已核对 命中行右侧第 6 列打标记 Set hit rng.FindNext(hit) Loop While Not hit Is Nothing And hit.Address firstAddr Debug.Print 共命中 n 处 End SubLookAt:xlPart是包含匹配xlWhole是整格相等默认是前者做编码匹配时如果不改会误命中一批前缀相同的记录。MatchCase控制大小写。另外Find会记住上一次调用时的参数如果上次设置过xlWhole又没显式传参这次就可能查不到——所以每次调用都把关键参数写全别指望默认值。字符串模式匹配用Like更直接尤其是格式校验Sub LikeCheck() Dim v As String, i As Long For i 2 To 100 v CStr(Cells(i, 1).Value) 一个字母开头 6 位数字例如 A123456 If v Like [A-Za-z]###### Then Cells(i, 2).Value 格式正确 Next i End Sub方括号里是字符集合#代表任意数字?代表任意单个字符*代表任意长度。它比正则轻得多格式规则简单的场景用它足够复杂规则才需要去 VBE 的「工具 → 引用」里勾选脚本运行库引正则。批量替换走Range.Replace比循环单元格快一个数量级Sub BatchReplace() Dim ws As Worksheet Set ws ThisWorkbook.Worksheets(明细) ws.Range(A2:A LastRowFind(ws, 1)).Replace _ What:有限公司, Replacement:, _ LookAt:xlPart, SearchOrder:xlByRows, MatchCase:False End Sub3.4 合并单元格与 Worksheet_Change 的 Target 参数合并单元格是表格里最漂亮、代码里最麻烦的东西合并后只有左上角那格有值其余全空删除行、排序、循环遍历都会出错。取消合并时要保留内容的正确写法是先接住整个合并区域Sub UnmergeKeepValue() Dim c As Range, m As Range, v As Variant For Each c In Selection If c.MergeCells Then Set m c.MergeArea 先记住整块取消合并之后 MergeArea 就只剩一格了 v c.Value m.UnMerge m.Value v 整块回填同一个值 End If Next c End Sub如果按直觉写成c.UnMerge之后c.MergeArea.Value v结果是把值写回了左上角一格其余格子依旧是空的——因为 UnMerge 之后MergeArea只返回它自己。这个先后顺序必须颠倒过来我在这上面查过半小时。判断区域里有没有合并单元格用Range.MergeCells注意它返回 True 的条件是「这个区域本身就是一个完整的合并块」拿一个大区域去判断常常得到 False。稳妥的做法是遍历其中的格子挨个判断。工作表事件里的Target参数同理它可能是一个格子也可能是一整片用户一次性粘贴几千行甚至还可能是带合并区域的单元格。所以处理前必须限流Private Sub Worksheet_Change(ByVal Target As Range) If Target.Cells.CountLarge 1 Then Exit Sub 批量粘贴直接放过 If Intersect(Target, Me.Columns(D)) Is Nothing Then Exit Sub 只管 D 列 On Error GoTo Finally Application.EnableEvents False 避免自己触发自己 Target.Offset(0, 1).Value Format(Now, yyyy-mm-dd hh:nn) 写入修改时间 Finally: Application.EnableEvents True 无论如何都要恢复 End SubIntersect(区域1, 区域2)返回两者的交集为空说明这次变化跟我们关心的列无关。CountLarge是为了防止大区域时Count溢出是老手才会注意的细节。EnableEvents False必须在写值之前打开否则写值这个动作又会触发一次 Change 事件形成递归最后 Excel 直接卡死。配套的On Error GoTo Finally保证中途报错时事件开关能恢复不然会一直处于关闭状态后面所有事件都像失联一样。4. Worksheet 与 Workbook 层增删、隐藏、保护与跨簿取数单表脚本写顺了迟早要面对多表多簿。这一层的难点不在于语法在于「引用的稳定性」和「动作的可撤销性」——删错一张表、关错一本簿CtrlZ是救不回来的。4.1 工作表引用的四种方式与两种遍历写法引用的四种方式各有代价按稳定性从高到低排大概是代码名称 名称 索引号 活动表。Sub SheetRef() Dim ws As Worksheet Set ws ThisWorkbook.Worksheets(明细) 按名称最常用 Debug.Print ThisWorkbook.Worksheets(1).Name 按索引号用户拖动表顺序就变了不建议写死 Debug.Print Sheet1.Name 按代码名称改表名也不影响最稳 Debug.Print ActiveSheet.Name 活动表状态不可控 For Each ws In ThisWorkbook.Worksheets 遍历写法一推荐 If ws.Visible xlSheetVisible Then Debug.Print ws.Name Next ws Dim i As Long For i 1 To ThisWorkbook.Worksheets.Count 遍历写法二注意删除表时要倒序 Debug.Print ThisWorkbook.Worksheets(i).Name Next i End Sub代码名称在 VBE 的工程窗口里能看到默认是 Sheet1、Sheet2 这种可以在属性窗口F4里改。用户改表名是家常便饭代码名称他改不了所以核心脚本挂代码名称更安全。用索引号遍历的前提下如果要删除表必须从Count往 1 倒着循环正序删会跳表——删掉第 3 张之后原来的第 4 张就变成第 3 张了循环变量再加 1 正好越过去。4.2 禁止删除、深度隐藏、防止改名保护类需求的实现给模板加保护本质是在合适的事件里拦截用户动作。禁止删除指定表靠SheetBeforeDelete事件里的Cancel 这段必须写在 ThisWorkbook 模块里放标准模块不生效 Private Sub Workbook_SheetBeforeDelete(ByVal Sh As Object) If Sh.Name 汇总 Or Sh.Name 参数 Then MsgBox 「 Sh.Name 」是系统表不允许删除, vbExclamation Cancel True End If End SubCancel True在较老的版本里对删除事件无效只能退而求其次用「工作簿结构保护」把整个增删表的入口锁住。这也顺带解决了另一个需求——防止改名Sub LockStructure() ThisWorkbook.Protect Password:admin123, Structure:True 只锁结构不锁窗口 End SubStructure:True锁的是工作簿结构用户可以继续编辑单元格、排序、筛选但不能增删表、改名、移动。改代码里的密码要跟实际使用的保持一致忘记密码基本等于工作簿结构永久锁死没有后悔药。深度隐藏是xlSheetVeryHidden它和普通隐藏的区别是右键菜单的「取消隐藏」里看不到只能在 VBE 的属性窗口把 Visible 改回xlSheetVisible才能恢复。Sub DeepHide() ThisWorkbook.Worksheets(参数).Visible xlSheetVeryHidden End Sub资料里有个很实用的组合技把配置表设为深度隐藏在Workbook_Open里再隐藏一次同时那张表上写着「请启用宏」。用户在打开时选择禁用宏Open 事件不执行配置表就露出来并被看到——这是「禁用宏则关闭工作簿」这类需求的典型实现思路本质上是拿可见性当信号灯。4.3 关闭不弹保存框与 BeforeClose 的取舍自动化流程跑到最后一步弹出一个「是否保存」对话框整个无人值守的设想就作废了。处理方式有两种选哪种取决于你想不想留下修改。 方式一关闭时强制保存 Private Sub Workbook_BeforeClose(Cancel As Boolean) If Not ThisWorkbook.Saved Then ThisWorkbook.Save End If End Sub 方式二代码主动关闭明确丢弃修改 Sub CloseNoAsk() Application.DisplayAlerts False ThisWorkbook.Close SaveChanges:False Application.DisplayAlerts True End Sub方式一的关键是ThisWorkbook.Saved这个属性它标记「自上次保存后有没有改动」比直接无脑 Save 高效也不会有多余的写盘。方式二里DisplayAlerts必须在关闭之后立刻改回来如果中途报错跳过了恢复那一行后面所有需要弹确认的操作删除表、覆盖文件都会静默执行很容易酿成事故。所以我一般写成On Error GoTo Finally加Finally: Application.DisplayAlerts True的结构。保存指定工作表为单独文件也是同一类需求用Sheets.CopySub SaveSheetAsBook() Dim p As String p ThisWorkbook.Path \导出_ Format(Now, yyyymmdd_hhnn) .xlsx ThisWorkbook.Sheets(汇总).Copy 不传参数就是复制到一个新工作簿 ActiveWorkbook.SaveAs p, FileFormat:xlOpenXMLWorkbook ActiveWorkbook.Close False End Sub注意Copy之后ActiveWorkbook已经变成新书了原来那本降到后台接着所有不带前缀的 Range 都会指向新书——这也是前面反复强调要显式声明工作簿对象的原因。4.4 跨簿取数五种方案的取舍资料第 3 章后半段列了五种不打开工作簿就取数的方案实际选型按「要不要实时」「数据量多大」「文件在不在本机」三个维度定方案是否打开源文件适用场景主要限制公式外部引用否少量单元格、要求实时更新路径写死源文件移动即断链GetObject后台打开结构复杂、要读取格式大文件慢容易残留进程隐藏 Application后台打开批量处理多个文件必须成对恢复 Visible否则 Excel 隐身ExecuteExcel4Macro否只取单个值、追求速度语法古老只能取数值ADO/SQL 连接否大表按条件筛选、做汇总需要引用数据访问库字段类型要处理数据量在几万行以内、只取一个汇总数用 GetObject 最省事几十个文件批量抽取我会用隐藏 Application 的写法但一定在On Error的收尾里把Application.Visible True补回来。曾经有一次调试中断在这一行之前整个 Excel 界面消失只能在任务管理器里结束进程那次之后我把恢复语句全部挪到了Finally段。Sub GetValueFromClosedBook() Dim p As String, wb As Workbook, v As Variant p ThisWorkbook.Path \源数据.xlsx Application.ScreenUpdating False On Error GoTo Finally Set wb GetObject(p) 后台加载不显示窗口 v wb.Worksheets(明细).Range(B2).Value wb.Close False ThisWorkbook.Worksheets(汇总).Range(B2).Value v Finally: Application.ScreenUpdating True End Sub5. 避坑排查宏点不动、事件不触发、保存后代码消失这五个现场这一章是血泪经验合集。下面五条都是我在实际项目里真实翻过车的每条按「看到什么 → 为什么 → 怎么修」写遇到相似症状可以直接对号入座。5.1 运行到 Select 就报 1004现象Range(A1).Select报「Select 方法作用于 Range 类失败」错误号 1004代码其他地方看着都没问题。原因Select 只能作用于活动工作表上的区域如果Range挂在一张非活动表上或者执行时活动表根本不是你引用的那张就必失败。另一种情况是工作簿处于保护状态选择被禁止。解决首选根本不用 Select直接对区域赋值绝大多数场景压根不需要「先选中再操作」确实必须选中的先ws.Activate再ws.Range(A1).Select。Sub SafeWrite() Dim ws As Worksheet Set ws ThisWorkbook.Worksheets(明细) ws.Range(A1).Value 直接写 不需要激活、不需要选中 End Sub5.2 改了单元格Worksheet_Change 不触发或者卡死现象手动改 D 列能触发运行代码改就不触发或者触发一次之后 Excel 直接转圈无响应。原因一是Application.EnableEvents被某段代码关掉之后没恢复常见于中途报错跳过了恢复语句二是事件处理程序内部又去改了单元格形成递归这是卡死的元凶。解决所有改单元格的事件代码都用On Error GoTo Finally包起来Finally段无条件恢复EnableEvents写值之前先把开关关掉。调试阶段可以在立即窗口执行Application.EnableEvents True手动救回来不用重启 Excel。5.3 关掉再打开写的代码全没了现象昨天写好的脚本今天打开文件点按钮没反应按 AltF11 进去一看模块是空的。原因文件被另存成了.xlsx。这个格式不支持 VBA 工程保存时 Excel 会弹一次提示但很多人顺手点了「是」代码就丢了。另存为.xlsm才是正确选项。解决养成习惯新建文件第一件事就是另存为.xlsm再做其他设计。另外把常用脚本集中放在个人宏工作簿或加载宏里和业务文件解耦业务文件怎么另存都不影响工具本体。5.4 Find 明明有值却返回 Nothing现象A 列肉眼可见有「华东」Find(华东)返回 Nothing或者只在某几段区域里找不到。原因三个高发点。LookIn默认是xlFormulas如果那些单元格是公式且公式文本里没有「华东」就搜不到After参数默认从区域第一格之后开始导致第一格永远被跳过Find会沿用上一次调用的参数前一次设过xlWhole没显式复位这次就变成整格匹配。解决每次调用把LookIn、LookAt、SearchDirection、After全部写全不依赖默认值。Set hit rng.Find(What:华东, _ After:rng.Cells(1, 1), _ LookIn:xlValues, _ LookAt:xlPart, _ SearchOrder:xlByRows, _ SearchDirection:xlNext, _ MatchCase:False)5.5 删除行之后数据整体错位少删了一行现象循环删除符合条件的行结果每次都会漏掉紧邻的下一行或者删完之后还有漏网的。原因正序循环删除时删掉第 i 行之后原来的第 i1 行上移顶到第 i 行而循环变量已经加到了 i1正好把它跳过去。解决两种写法一是从最后一行倒着循环到第 2 行二是用Union把所有命中行攒成一个大区域最后一次EntireRow.Delete。Sub DeleteBlankRows() Dim i As Long Application.ScreenUpdating False For i Cells(Rows.Count, 1).End(xlUp).Row To 2 Step -1 倒序删不会跳行 If Trim(Cells(i, 1).Value) Then Rows(i).Delete Next i Application.ScreenUpdating True End Sub倒序删的另一个好处是可以省掉屏幕刷新一万行以内基本秒完。批量删除之前务必先备份一份删行是没法撤销的。6. 二次开发把 70 个技巧收进自己的加载宏工具箱零散脚本写到一定数量一定会想「为什么每次新项目都要重新复制这些函数」。我现在的做法是建一个.xlsam加载宏把资料里的技巧按职责拆成四个标准模块modRange放区域查找、边界定位、复制粘贴modSheet放增删表、遍历、保护modBook放跨簿取数和保存modUtil放日志、路径、错误处理这些杂项。加载宏的好处是所有工作簿打开时都能调用且业务文件怎么另存都不影响工具本身。真正让工具箱好用的是两件事所有列号集中管理所有入口带错误兜底。 modConst表结构只在这一处描述字段位置变了只改这里 Public Const COL_DATE As Long 1 Public Const COL_REGION As Long 3 Public Const COL_AMOUNT As Long 6 modUtil带日志的执行壳出错时能追溯是哪一步挂的 Public Sub LogIt(ByVal msg As String) Dim f As Integer f FreeFile Open ThisWorkbook.Path \run.log For Append As #f Print #f, Format(Now, yyyy-mm-dd hh:nn:ss) msg Close #f End Sub Public Function LastRow(ByVal ws As Worksheet, ByVal col As Long) As Long Dim r As Range Set r ws.Columns(col).Find(*, ws.Cells(1, col), xlFormulas, xlPart, , xlPrevious) If r Is Nothing Then LastRow 0 Else LastRow r.Row 空列返回 0调用方要判 End FunctionmodConst里那几个常量看着不起眼但它是整份脚本能不能撑过半年的分界线。业务表加一列、调一次顺序是常事常量集中之后改一行就够散落在各处的Cells(i, 6)才是维护灾难。LastRow用 Find 而不是 End(xlUp)是为了避开整列为空返回 1 的老坑返回 0 让调用方显式处理空表。改造顺序上我的建议是先挑一个每天都要跑的手工流程用它跑通「加载宏 常量表 日志」这条链路再往里加功能。上来就照着 70 个技巧全实现一遍通常做到第 20 个就没动力了。验证方法很简单把同一个流程手工做一遍、脚本做一遍比对结果行数和关键字段的合计值对不上就去看run.log的最后一个时间戳问题基本就在那之后几步之内。还有个小技巧值得单独说把第 5 章那些排查语句做成一个Sub Diag()挂在加载宏里内容包括当前活动表名、UsedRange 地址、EnableEvents 状态、行列数统计遇到「跑不动」先执行它五行输出往往就能定位到是环境问题还是数据问题。从那以后我每接一张新表都先把它的字段位置抄进modConst再动第一行代码宁可多花十分钟也不让硬编码散出去。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站