VBA宏总崩溃?Python脚本老闪退?这招让你的代码从此“抗摔”!
off999 2025-09-01 11:26 20 浏览 0 评论
导语:
上周有个读者急吼吼地私信我:“老师,我写了个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 < 0 Then
Err.Raise vbObjectError + 999, , "销售额不能为负!" ' 主动抛错(自定义错误)
End If
commission = sales * 0.1 ' 可能出错点3:sales是文本格式(类型错误)
ws.Cells(i, "B").Value = commission
Next i
MsgBox "计算完成!", vbInformation
Exit Sub ' 正常结束,跳过错误处理区
' 2. 错误处理区(急救站)
ErrorHandler:
' 显示具体错误信息(错误号+描述+来源)
MsgBox "错误号:" & Err.Number & vbCrLf & _
"错误原因:" & Err.Description & vbCrLf & _
"错误来源:" & Err.Source, vbCritical, "程序异常"
' 可选操作:记录错误日志(写入文本文件)
Open ThisWorkbook.Path & "\错误日志.txt" For Append As #1
Print #1, "[" & Now() & "] 错误号:" & Err.Number & ";描述:" & Err.Description
Close #1
' 清除错误状态,避免影响后续代码
Err.Clear
End Sub
这段代码能处理哪些崩溃?
- 如果“数据源”工作表被删除,触发错误号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 "临时文件清理完成(可能有遗漏)", vbInformation
End Sub
VBA错误处理哲学: 像老司机开车——提前检查路况(预判错误点),出事故立刻靠边停车(跳转处理区),修好后继续上路(清除错误状态)。
第二部分:Python——用“精密陷阱”捕获所有异常
Python的错误处理更像“工程师设计精密仪器”:提前预判所有可能的故障点,为每种故障定制“专属陷阱”,程序跑着跑着出错了?陷阱自动触发,稳稳接住! 它的核心是
try...except...else...finally结构,这是Python程序员的“防崩秘籍”。
1. 四大利器:
try...except...else...finally
- try:放可能出错的“危险代码”;
- except:针对不同错误类型(如除零、文件不存在)定制陷阱;
- else:如果try里的代码没出错,执行这里的“成功逻辑”;
- finally:无论是否出错,最后一定要执行的“收尾动作”(如关闭文件、释放资源)。
import xlwings as xw
import logging # 导入日志模块,专业记录错误
# 配置日志:将错误写入文件(比弹窗更专业)
logging.basicConfig(filename='error.log', level=logging.ERROR,
format='%(asctime)s - %(levelname)s - %(message)s')
def 计算佣金(sales):
try:
# 危险操作1:读取Excel单元格(可能为空、非数值)
if sales < 0:
raise ValueError("销售额不能为负数!") # 主动抛自定义异常
return sales * 0.1
except ZeroDivisionError:
logging.error("除零错误:销售额为0,无法计算佣金")
return 0
except TypeError as e:
logging.error(f"类型错误:输入应为数值,实际是{type(sales)},错误详情:{e}")
return None
except ValueError as e:
logging.error(f"自定义错误:{e}")
return None
except Exception as e: # 兜底陷阱,捕获所有未预期的错误
logging.error(f"未知错误:{e}", exc_info=True) # 记录完整堆栈信息
return None
else:
logging.info("佣金计算成功") # 仅当无错误时执行
finally:
print("佣金计算流程结束(无论成功与否)") # 必执行的收尾动作
# 主程序:循环处理数据
def 批量处理():
try:
wb = xw.books.open("销售数据.xlsx") # 危险操作2:打开不存在的文件
sheet = wb.sheets["数据"]
last_row = sheet.range("A" + str(sheet.cells.last_cell.row)).end("up").row
for row in range(2, last_row + 1):
sales = sheet.range(f"A{row}").value
commission = 计算佣金(sales)
sheet.range(f"B{row}").value = commission
except FileNotFoundError:
logging.critical("文件未找到:请检查'sales数据.xlsx'路径是否正确")
except PermissionError:
logging.critical("权限不足:请关闭Excel文件后重试")
except Exception as e:
logging.critical(f"批量处理失败:{e}", exc_info=True)
finally:
try:
wb.close() # 无论是否出错,尝试关闭工作簿
except:
pass # 如果wb未成功打开,忽略此错误
批量处理()
这段代码有多“抗造”?
- 文件不存在?触发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 Sub
ErrorHandler:
MsgBox "错误:" & Err.Description & vbCrLf & "请联系管理员。", vbCritical
' 记录日志(同上,此处省略)
Err.Clear
End Sub
Python“防崩版”解法:
import xlwings as xw
import logging
logging.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
生成奖金表()
终极对比:VBA与Python的错误处理谁更强?
特性 | VBA | Python |
错误捕获方式 | On Error GoTo跳转 | try...except精准捕获 |
错误类型支持 | 仅内置错误号(如Error 1004) | 支持自定义异常类,类型更灵活 |
日志记录 | 需手动写文件(麻烦) | 内置logging模块(专业) |
代码可读性 | 错误处理与业务逻辑混杂(易乱) | 异常块独立(结构清晰) |
适用场景 | 小范围、单一功能的Excel宏 | 复杂系统、需要多人协作的脚本 |
比喻 | 老司机的“应急工具箱” | 工程师的“精密故障排查系统” |
千万别学Excel结语:错误处理,是程序员的“职业尊严”
从今天起,请记住:
- 写VBA时,每个Sub和Function的第一行都加上On Error GoTo,别让崩溃成为习惯;
- 写Python时,每个try块都明确列出可能出现的异常类型,别让“万能except”毁了代码的可维护性;
- 无论用什么语言,日志记录都是你的“后悔药”——出错了没关系,但至少要知道怎么错的。
最后送你一句话: 用户不会因为你处理了错误而夸你,但一定会因为你没处理错误而骂你。而专业的程序员,连“被骂的机会”都不给自己。
互动话题: 你在写代码时遇到过最离谱的崩溃是什么?是因为单元格格式错了?还是文件被同事误删了?在评论区分享你的“血泪史”!
Excel #Python #VBA #错误处理 #编程技巧 #办公自动化 #代码健壮性
【测试题】3道题测测你的错误处理水平
- VBA中,用于跳转到错误处理代码块的关键字是? A. On Error Resume Next B. On Error GoTo C. Exit Sub D. Err.Clear
- Python中,若要捕获“文件未找到”错误,应使用哪个异常类? A. TypeError B. ValueError C. FileNotFoundError D. ZeroDivisionError
- 以下哪项是错误处理的核心目的? A. 让代码永远不报错 B. 让程序崩溃时更美观 C. 在出错时优雅应对,减少损失 D. 提高代码运行速度
【答案】
- B(On Error GoTo用于跳转到错误处理标签)
- C(FileNotFoundError专门捕获文件未找到错误)
- C(错误处理的目的是在出错时减少损失,而非完全避免错误)
欢迎关注:千万别学excel,这里有数千篇excel相关视频和文章,带你玩转excel!祝你早日升任CEO,迎娶白富美,走上人生巅峰~( ̄▽ ̄~)~
相关推荐
- u盘在电脑上怎么找出来(u盘在电脑上怎么找到)
-
在电脑中找不到u盘,是因为系统没有自动识别出来,手动打开即可,具体的解决步骤如下:1、在桌面上点击我的电脑,右键,管理。2、打开管理界面,点击储存。3、进到储存页面。4、到这一步,也就可以看到了,有这...
- 联想一体机怎么进入bios(联想一体机怎么进入u盘启动)
-
所需工具:联想Lenovo品牌一体机、启动U盘。具体步骤如下:1、联想一体机从U盘启动设置步骤如下重启联想一体机,启动过程中按F1进入BIOS,部分机型则是开机按Enter键,进入之后再按F12选择进...
- 如何装ghost系统盘(ghost装机教程)
-
ghost是不能做系统c盘,它是一种对硬盘和分区制作成映像文件进行备份和恢复的工具软件,是不能进行操作系统安装。这个软件的使用目的是,当我们安装配置好操作系统以后,用ghost软件对c盘进行备份,或者...
- 加密u盘如何格式化(加密u盘如何格式化手机)
-
1,点击系统与安全进入电脑的控制面板界面,点击上方的系统与安全的选项,在系统界面找到最下方的管理工具功能组。2,选中u盘选择管理工具下面的创建并格式化硬盘分区,点击弹出磁盘管理的界面,在这个里面选中你...
- 万能显卡驱动离线版pc(万能显卡驱动离线版)
-
万用驱动是综合各电脑硬件的性能而制做的软件,对于大多数的电脑硬件驱动都好用,但对于少数品牌电脑驱动要求严格的,就不灵了。有的硬件用万能驱动后,使用效果不佳,就是因为没有完全驱动好。所以,知名品牌电脑硬...
- 笔记本windows8系统下载(笔记本电脑系统win8)
-
在电脑上面就可以下载,打开浏览器搜索windous8系统会出现一些下拉选择,选择第一条或者选择有官网字样的,就直接有下载按钮,然后点击下载就可以了win8可以支持现在可以见到的所有Photosho...
- win 11(win 11 25h2)
-
Windows11是由微软公司(Microsoft)开发的操作系统,应用于计算机和平板电脑等设备。于2021年6月24日发布,2021年10月5日发行。Windows11提供了许多创新...
- 手机视频恢复软件免费版下载
-
手机视频删了怎么恢复 一、安卓手机视频恢复 1.打开电脑,移动鼠标,进入互盾安卓恢复大师官网,下载并安装该软件。手机连接至电脑。手机视频删了怎么恢复 2.打开运行互盾安卓恢复大师,在软件界面看到...
- diy电脑装机教程(diy电脑组装步骤)
-
1,看价格。根据自己的预算价格,选择适合该价格的电脑。注意不要以过高的价格买到配置过低的电脑;2,看性能。根据自己需要的电脑性能,以合理的价格购买。注意不要以过高的价格买到配置过低的电脑。电脑的配置如...
- u盘莫名其妙要格式化(u盘总是要格式化什么意思)
-
如果您在使用U盘时突然收到提示需要格式化的消息,这可能是由于以下原因之一引起的:U盘感染病毒:U盘中可能存在恶意病毒,这些病毒可能会导致U盘无法正常使用。当您尝试打开U盘时,系统会提示您进行格式化操作...
- win7家庭版原版(win7家庭版价格)
-
你的win7旗舰版应该是个盗版软件,在你使用的过程中你可能触碰到了后台升级,升级完以后就变成了家庭版了,在你不知不觉中被改变的,厄这个软件属于盗版的,厄升级完以后没什么大区别,这个旗舰版家庭版在家里面...
- win10自动更新失败怎么办(win10自动升级失败)
-
安装更新失败有许多原因。WindowsUpdate需要能够扫描您的计算机以了解需要哪些更新,并能够下载和安装这些更新。如果某个阶段遇到问题,则可能阻止某个更新安装到计算机中。有关错误或失败的详细信...
- 截图的几种方法(截图的几种方法有哪些)
-
第一种截图方式:按printScreen键。按一下键盘上的printScreen键以后,整个屏幕会被截取下来,截图会默认保存在剪贴板中。第二种截图方式:使用微信截图。进入聊天界面,我们会发...
- 电脑装了两个系统怎么切换(电脑安装2个系统怎么更换启动)
-
1.点击运行打开电脑点击左下角的开始菜单栏选项,右击鼠标在序列栏中选择运行打开。2.输入msconfig接着在运行的输入框中输入msconfig点击确定即可打开系统配置。3.点击引导打开系统配置的页面...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
python入门到脱坑 输入与输出—str()函数
-
宝塔面板如何添加免费waf防火墙?(宝塔面板开启https)
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
慕ke 前端工程师2024「完整」
-
失业程序员复习python笔记——条件与循环
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
