VBA宏总崩溃?Python脚本老闪退?这招让你的代码从此“抗摔”!
off999 2025-09-01 11:26 4 浏览 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,迎娶白富美,走上人生巅峰~( ̄▽ ̄~)~
相关推荐
- 一招实现让Python Numpy快到飞起!
-
在本文中,笔者将介绍Numexpr这个强大的Python库,它可以显著提升NumPy数组的计算性能。Numexpr是一个专门用于提高NumPy数组计算性能的工具。它通过避免为中间结果分配内存,提高了缓...
- 通过anaconda安装python及人工智能框架pytorch安装
-
专栏推荐python2和python3的切换Linux安装好了之后,一般有python2和python3,linux默认的是python2,但是现在python可能马上就要被淘汰了,所以我们平时都是使...
- VBA宏总崩溃?Python脚本老闪退?这招让你的代码从此“抗摔”!
-
导语:上周有个读者急吼吼地私信我:“老师,我写了个VBA宏整理半年数据,结果跑到第800行的时候弹出个Error1004,前功尽弃!有没有办法让宏‘抗摔’?”还有位Python新手更崩溃:“明明写了...
- 六种程序设计语言的循环及循环嵌套之比较(周兴富)
-
我在《72岁老翁学python》(六)一文里展示了仿写的第一个python程序zxf-python-cx01.py,以及试写的一个程序zxf-python-cx02.py,虽然错误多多,但是万里长征第...
- 简单学Python——关键字6——break和continue
-
Python退出循环,有break语句和continue语句两种实现方式。break语句和continue语句的区别:break语句作用是终止循环。continue语句作用是跳出本轮循环,继续下一次循...
- Python循环的简单写法_python 循环写法
-
在之前的短文里归纳总结了Shell的循环写法,里面也提到了Python的循环,这里也对Python的循环做个简单的说明。Python的循环基本上2种:while循环和for-in循环,和Shell还是...
- Python循环语句:从基础到进阶_python里面的循环语句
-
在Python编程语言中,循环语句是必不可少的控制流工具。它们允许重复执行一段代码,直到满足特定条件。本文将介绍Python中的两种主要循环类型:for循环和while循环,并分别结合实例来说明其用法...
- Python教程(十):for循环和range()函数
-
今天我们将深入探讨非常有用的**for循环和内置的range()函数**—这两个工具让您能够重复执行操作并高效地遍历序列。让我们探索如何使用它们以及它们在哪些地方表现出色。今天您将学习什么什么...
- 《循环(for/while)》_循环for的用法
-
循环(for/while)循环是编程中处理重复任务的核心工具,Python提供了两种主流循环结构:for循环(遍历可迭代对象)和while循环(根据条件重复执行)。本节将系统讲解两者的语法、使用场景及...
- Python循环结构精解:掌握while循环与累加算法
-
循环结构是程序三大基本结构之一,其本质是反复执行相同操作直到满足特定条件。现实世界中的循环无处不在:旋转的车轮人体的血液循环每日重复的学习过程Python循环分类while循环:基于条件判断的循环fo...
- Python教程(十一):循环控制 – break、continue和pass
-
昨天,我们探索了for循环和range()函数。今天,我们将更进一步,学习如何使用三个强大的工具来控制循环内部的流程:break、continue和pass。这些命令让您能够:停止循环(break)...
- AI最火语言python之循环结构_python语言中的循环结构
-
循环结构是指在某种情况下反复执行某段代码,其中被反复执行的代码段称之为循环体,循环结构主要负责完成重复性的工作。有for循环语句和while循环语句实现循环结构。1.for循环语句for语句主要用于实...
- Python入门学习教程:第 5 章 循环语句
-
5.1什么是循环语句?在编程中,我们经常需要重复执行某段代码。例如,计算1到100的总和、打印列表中的所有元素等。如果手动编写每一次的执行代码,不仅繁琐,还容易出错。循环语句就是用来解决这种...
- Python之Web开发框架学习-Django基础及安装
-
Django是一个高级PythonWeb框架,鼓励快速开发和简洁实用的设计。Django使使用更少的代码快速构建更好的Web应用程序变得更加容易。注-Django是DjangoSoftware...
- Python基础入门- Python编程环境搭建-Python编译器安装
-
Python3下载访问https://www.python.org/进行下载Python安装双击下载的“.exe”文件即可安装手动配置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)