摘要:上周有个读者急吼吼地私信我:“老师,我写了个VBA宏整理半年数据,结果跑到第800行的时候弹出个Error 1004,前功尽弃!有没有办法让宏‘抗摔’?”还有位Python新手更崩溃:“明明写了检查空值的代码,结果因为一个单元格是文本格式,整个脚本直接闪退,半
导语:
上周有个读者急吼吼地私信我:“老师,我写了个VBA宏整理半年数据,结果跑到第800行的时候弹出个Error 1004,前功尽弃!有没有办法让宏‘抗摔’?”还有位Python新手更崩溃:“明明写了检查空值的代码,结果因为一个单元格是文本格式,整个脚本直接闪退,半小时白干……”
别慌!这不是你技术菜,而是没掌握编程圈的“保命神技”——错误处理。今天第五讲,我用20年编程经验总结的“防崩指南”,手把手教你让vbA和Python代码从“玻璃心”变“金刚不坏”,从此告别“改不完的bug,流不完的眼泪”!
第五讲:错误处理——让代码“稳如老狗”的终极法则
程序员圈有句扎心真相:“完美的代码不存在,但能优雅处理错误的代码才专业。” 无论是VBA还是Python,错误处理不是“加分项”,而是“生存线”。它能让你在用户骂“这破程序又崩了”时,淡定地说:“我早料到了。”
第一部分:VBA——用“安全网”兜住所有意外
VBA的错误处理逻辑像极了“老司机的应急反应”:预判可能翻车的点,提前铺好安全网,出事了就顺着网爬回安全区。 它的核心工具是On Error语句,这是VBA程序员的“保命符”。
1. 必杀技:On Error GoTo——精准跳转错误处理区
这是VBA最经典的错误捕获模式,相当于给代码装了个“GPS导航”:主逻辑跑着跑着出错了?立刻跳转到指定标签的“急救站”处理。
Sub 批量计算佣金 ' 1. 启动“安全模式”:一旦出错,跳转到ErrorHandler标签 On Error GoTo ErrorHandler Dim ws As Worksheet Set ws = ThisWorkbook.Sheets("数据源") ' 可能出错点1:工作表不存在 Dim lastRow As Long lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row ' 可能出错点2:A列全空 Dim i As Long, sales As Double, commission As Double For i = 2 To lastRow sales = ws.Cells(i, "A").Value If sales这段代码能处理哪些崩溃?
如果“数据源”工作表被删除,触发错误号9:下标越界,跳转到处理区并记录日志;如果A列全是空值,lastRow会是1,循环从2到1不执行,但不会报错;如果A列有文本(如“无效数据”),sales = ws.Cells(i, "A").Value会触发错误号13:类型不匹配,同样被捕获;主动用Err.Raise抛出的自定义错误(如销售额为负),也会被精准捕获。2. 进阶技巧:On Error Resume Next——小心使用的“免死金牌”
这条语句的意思是:“出错了?别管,继续执行下一句。” 它适合处理“无关痛痒”的小错误(比如删除一个不存在的文件),但新手慎用——用不好会让程序“带病运行”,埋下更大隐患。
Sub 清理临时文件 On Error Resume Next ' 开启“免死金牌” Kill "C:\Temp\old_report.xlsx" ' 文件不存在?忽略错误 Kill "D:\Backup\temp.xls" ' 路径错误?也忽略 On Error GoTo 0 ' 关闭“免死金牌”,恢复严格错误提示 MsgBox "临时文件清理完成(可能有遗漏)", vbInformationEnd Sub✔️ VBA错误处理哲学: 像老司机开车——提前检查路况(预判错误点),出事故立刻靠边停车(跳转处理区),修好后继续上路(清除错误状态)。
第二部分:Python——用“精密陷阱”捕获所有异常
Python的错误处理更像“工程师设计精密仪器”:提前预判所有可能的故障点,为每种故障定制“专属陷阱”,程序跑着跑着出错了?陷阱自动触发,稳稳接住! 它的核心是try...except...else...finally结构,这是Python程序员的“防崩秘籍”。
1. 四大利器:try...except...else...finally
try:放可能出错的“危险代码”;except:针对不同错误类型(如除零、文件不存在)定制陷阱;else:如果try里的代码没出错,执行这里的“成功逻辑”;finally:无论是否出错,最后一定要执行的“收尾动作”(如关闭文件、释放资源)。import xlwings as xwimport logging # 导入日志模块,专业记录错误# 配置日志:将错误写入文件(比弹窗更专业)logging.basicConfig(Filename='error.log', level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')def 计算佣金(sales): try: # 危险操作1:读取Excel单元格(可能为空、非数值) if sales这段代码有多“抗造”?
文件不存在?触发FileNotFoundError,记录日志并提示;权限不足?触发PermissionError,避免程序崩溃;销售额是文本?触发TypeError,记录类型错误详情;销售额为负?触发自定义ValueError,明确提示原因;所有操作结束后,无论成功与否,finally都会尝试关闭工作簿,防止资源泄露。2. 高手进阶:自定义异常类——让错误“更懂你”
Python允许你自定义异常类型,让错误信息更贴合业务场景。比如电商系统中,定义库存不足异常、支付超时异常,代码会更清晰。
class 库存不足异常(Exception): """当商品库存小于订单数量时抛出的异常""" def __init__(self, 商品名称, 当前库存, 订单数量): self.商品名称 = 商品名称 self.当前库存 = 当前库存 self.订单数量 = 订单数量 super.__init__(f"{商品名称}库存不足!当前库存{当前库存},订单需要{订单数量}")# 使用自定义异常def 下单(商品名称, 当前库存, 订单数量): if 订单数量 > 当前库存: raise 库存不足异常(商品名称, 当前库存, 订单数量) print("下单成功!")# 测试try: 下单("手机", 10, 15)except 库存不足异常 as e: print(f"下单失败:{e}") # 输出:下单失败:手机库存不足!当前库存10,订单需要15✔️ Python错误处理哲学: 像工程师设计电路——每个可能的故障点都有对应的保险丝(except),主电路(try)负责正常工作,保险丝熔断(捕获错误)时启动备用方案(处理逻辑),最后无论是否熔断都要切断总电源(finally)。
核心实战:用错误处理“复活”崩溃脚本
场景: 你需要写一个脚本,从Excel读取员工绩效分,计算奖金(绩效分×1000),并将结果写入新工作表。但实际数据中可能存在:
绩效分是文本(如“优秀”);绩效分超过100(系统限制最高100);目标工作表已存在(需先删除旧表)。VBA“防崩版”解法:
Sub 生成奖金表 On Error GoTo ErrorHandler Dim wsSource As Worksheet, wsTarget As Worksheet Set wsSource = ThisWorkbook.Sheets("绩效数据") Dim lastRow As Long: lastRow = wsSource.Cells(wsSource.Rows.Count, "A").End(xlUp).Row ' 检查目标表是否存在,存在则删除(可能出错点:工作表被保护) On Error Resume Next Application.DisplayAlerts = False ' 关闭删除确认弹窗 ThisWorkbook.Sheets("奖金表").Delete Application.DisplayAlerts = True On Error GoTo ErrorHandler ' 创建新目标表 Set wsTarget = ThisWorkbook.Sheets.Add(After:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)) wsTarget.Name = "奖金表" wsTarget.Range("A1:B1").Value = Array("姓名", "奖金") ' 计算奖金 For i = 2 To lastRow Dim 姓名 As String, 绩效分 As Double, 奖金 As Double 姓名 = wsSource.Cells(i, "A").Value 绩效分 = wsSource.Cells(i, "B").Value ' 检查绩效分是否为数值 If Not IsNumeric(绩效分) Then Err.Raise vbObjectError + 100, , "绩效分必须为数值!" End If ' 检查绩效分是否超上限 If 绩效分 > 100 Then Err.Raise vbObjectError + 101, , "绩效分不能超过100!" End If 奖金 = 绩效分 * 1000 wsTarget.Cells(i, "A").Value = 姓名 wsTarget.Cells(i, "B").Value = 奖金 Next i MsgBox "奖金表生成成功!", vbInformation Exit SubErrorHandler: MsgBox "错误:" & Err.Description & vbCrLf & "请联系管理员。", vbCritical ' 记录日志(同上,此处省略) Err.ClearEnd SubPython“防崩版”解法:
import xlwings as xwimport logginglogging.basicConfig(filename='奖金表错误.log', level=logging.ERROR)def 生成奖金表: try: wb = xw.books.open("绩效数据.xlsx") ws_source = wb.sheets["绩效数据"] last_row = ws_source.range("A" + str(ws_source.cells.last_cell.row)).end("up").row # 删除已存在的奖金表(可能出错点:表被保护/不存在) try: wb.sheets["奖金表"].delete except: pass # 表不存在时忽略 # 创建新奖金表 ws_target = wb.sheets.add("奖金表") ws_target.range("A1:B1").value = ["姓名", "奖金"] # 计算奖金 for row in range(2, last_row + 1): 姓名 = ws_source.range(f"A{row}").value 绩效分 = ws_source.range(f"B{row}").value # 检查绩效分是否为数值 if not isinstance(绩效分, (int, float)): raise TypeError(f"第{row}行绩效分非数值:{绩效分}") # 检查绩效分是否超上限 if 绩效分 > 100: raise ValueError(f"第{row}行绩效分超上限:{绩效分}") ws_target.range(f"A{row}").value = 姓名 ws_target.range(f"B{row}").value = 绩效分 * 1000 wb.save("奖金表.xlsx") wb.close print("奖金表生成成功!") except FileNotFoundError: logging.error("绩效数据.xlsx未找到,请检查路径") except PermissionError: logging.error("文件被占用,请关闭后重试") except (TypeError, ValueError) as e: logging.error(f"数据错误:{e}") except Exception as e: logging.error(f"未知错误:{e}", exc_info=True) finally: try: wb.close # 确保关闭工作簿 except: pass生成奖金表特性VBAPython错误捕获方式On Error GoTo跳转try...except精准捕获错误类型支持仅内置错误号(如Error 1004)支持自定义异常类,类型更灵活日志记录需手动写文件(麻烦)内置logging模块(专业)代码可读性错误处理与业务逻辑混杂(易乱)异常块独立(结构清晰)适用场景小范围、单一功能的Excel宏复杂系统、需要多人协作的脚本比喻老司机的“应急工具箱”工程师的“精密故障排查系统”千万别学Excel结语:错误处理,是程序员的“职业尊严”
从今天起,请记住:
写VBA时,每个Sub和Function的第一行都加上On Error GoTo,别让崩溃成为习惯;写Python时,每个try块都明确列出可能出现的异常类型,别让“万能except”毁了代码的可维护性;无论用什么语言,日志记录都是你的“后悔药”——出错了没关系,但至少要知道怎么错的。最后送你一句话: 用户不会因为你处理了错误而夸你,但一定会因为你没处理错误而骂你。而专业的程序员,连“被骂的机会”都不给自己。
互动话题: 你在写代码时遇到过最离谱的崩溃是什么?是因为单元格格式错了?还是文件被同事误删了?在评论区分享你的“血泪史”!
【测试题】3道题测测你的错误处理水平
VBA中,用于跳转到错误处理代码块的关键字是? A. On Error Resume Next B. On Error GoTo C. Exit Sub D. Err.ClearPython中,若要捕获“文件未找到”错误,应使用哪个异常类? A. TypeError B. ValueError C. FileNotFoundError D. ZeroDivisionError以下哪项是错误处理的核心目的? A. 让代码永远不报错 B. 让程序崩溃时更美观 C. 在出错时优雅应对,减少损失 D. 提高代码运行速度B(On Error GoTo用于跳转到错误处理标签)C(FileNotFoundError专门捕获文件未找到错误)C(错误处理的目的是在出错时减少损失,而非完全避免错误)来源:千万别学Excel