百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

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道题测测你的错误处理水平

  1. VBA中,用于跳转到错误处理代码块的关键字是? A. On Error Resume Next B. On Error GoTo C. Exit Sub D. Err.Clear
  2. Python中,若要捕获“文件未找到”错误,应使用哪个异常类? A. TypeError B. ValueError C. FileNotFoundError D. ZeroDivisionError
  3. 以下哪项是错误处理的核心目的? A. 让代码永远不报错 B. 让程序崩溃时更美观 C. 在出错时优雅应对,减少损失 D. 提高代码运行速度

【答案】

  1. B(On Error GoTo用于跳转到错误处理标签)
  2. C(FileNotFoundError专门捕获文件未找到错误)
  3. 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环境变量如果是在安装过程中,勾选了配置环境变量...

取消回复欢迎 发表评论: