上周帮同事处理一张四千多行的订单备注表,备注栏长这样:“张三 138****1234 上海市浦东新区XX路1号 购买3件白色L码”。他原本打算用Excel自带的查找替换、分列、LEFT加MID函数去硬拆,搞了一个下午,拆到后面发现手机号后面还跟着“(收货)”,地址里面又嵌着逗号,整个表格严重错位。我打开文件写了十几行VBA,配合正则表达式,十秒不到,姓名、电话、地址、件数、颜色尺码全部拆进独立列里,同事愣住,问我是不是用了什么外挂。

这就是Excel VBA和正则表达式组合起来最典型的价值。正则表达式这套匹配文本的规则,解决的核心问题就是“字符串不规整”:你要从一段乱七八糟的文本里提取手机号、金额、日期、身份证号,要按照规则批量替换掉多余内容,要判断某段文字是否符合格式——这些活如果全靠手工或者纯函数硬凑,写出来的公式又绕又脆,而交给正则,往往一两行代码就结束了。这一章内容对两类人最有用:一类是已经会VBA基础循环和单元格操作、但遇到复杂字符串就头疼的人;另一类是写过一点正则、但不知道怎么在VBA环境里落地、总是遇到报错的人。前几章我们聊过数组、字典和ADO的数据处理思路,这一章换个方向,专门研究文本处理的终极武器。

1. 为什么正则这么“香”:Excel原生文本方案的三个硬伤

1.1 查找替换和分列解决不了“结构不统一”的文本

先说一个最现实的场景:你的表格里有一列“备注”,内容长这样: 张三 13812341234 上海市浦东新区XX路1号 3件白色L码 。这一列看起来好像有规律,但仔细一看,有的行用空格分隔,有的行用逗号分隔,有的行中间还混着“联系人:张三”这样的前缀。这时候你用Excel的“分列”功能,指定空格作为分隔符,结果第一行拆对了,第二行拆出来5列,第三行又变成2列,整个表直接乱套。

查找替换就更不用说了。查找替换的本质是“精确匹配”或“通配符匹配”,它能处理“把所有‘张三’改成‘李四’”这种单一规则,但处理不了“把这段文字里所有13开头的11位数字都找出来”这种结构性需求。通配符里的 * 和 ? 能力太弱, * 只能表示“任意一串字符”,你没法告诉它“必须是数字”“必须刚好11位”“前面不能跟其他数字”这样的约束。所以只要数据没有统一的格式,手工肉眼拆就是无底洞,几千行能折腾一整天。

1.2 工作表函数能应付一部分,但写起来像天书

有基础的朋友可能会说,用LEFT、MID、RIGHT、FIND、SUBSTITUTE这些函数组合一下不就行了?确实,简单的场景可以。比如固定格式的“订单号ORD12345”,你知道“ORD”后面跟6位数字,那用 MID(A2,4,6) 就能提出来。但问题在于,一旦条件不固定,公式就开始爆炸。

我见过有人写过一个提取金额的公式,大概是 =IF(ISNUMBER(FIND("元",A2)),MID(A2,FIND("元",A2)-6,6),"") 这种,还要嵌套IFERROR、要判断小数点在不在、要判断负数有没有括号,最后写出来一个粮票一样长的公式,维护的人看到就想离职。更关键的是,函数方案一次性只能解决“这一列”的问题,换一个文本格式,整个公式推倒重写。

正则表达式的思路完全不同。它不关心文本的“位置”,只关心文本的“结构”。比如你要提取金额,模式就是“可能有负号、后面跟数字、可能有小数部分”,这和你文本前面是“实付”还是“应付”还是“价格:”没有关系。用结构描述替代位置计算,是文本处理思维上的一次升级。

1.3 这套引擎是Windows自带的,不需要额外装插件

很多人以为在Excel里用正则要装什么插件或者加载项,其实完全不需要。VBA调用的是微软VBScript正则表达式引擎,这个组件是Windows系统自带的COM对象,注册名是 VBScript.RegExp 。从Excel 2007到Office 365,从32位到64位,基本都稳定可用。你在VBA里写一句 CreateObject("VBScript.RegExp") ,正则能力就直接到手了。

这个方案还有个隐藏优势:跨宿主程序复用。同一套正则逻辑,你在Excel的VBA里能用,在Access的VBA里也能用,在WPS的宏编辑器里大概率也能用(前提是WPS版本支持宏)。我曾经把一套文本清洗的VBA代码从Excel原封不动搬到Access里处理数据库字段,正则部分一行没改,效果完全一致。所以花时间学这一章,收益不只是“会处理Excel表格”,而是掌握了一套通用的文本处理能力。

2. 第一次跑通VBA正则:对象、属性和四步调用套路

2.1 前期绑定还是后期绑定:先决定代码的写法

在VBA里调用正则,有两种写法。第一种是“前期绑定”:打开VBA编辑器,点菜单“工具→引用”,勾选 Microsoft VBScript Regular Expressions 5.5 ,然后代码里直接写 Dim reg As New RegExp 。这样写的好处是写代码的时候有自动提示,输入 reg. 会弹出属性方法列表,不容易拼错。坏处是:万一换了一台没有勾选这个引用的电脑,代码会直接报“用户定义类型未定义”,对新手不友好。

第二种是“后期绑定”:不勾选引用,直接用 CreateObject 创建对象。代码写成 Dim reg As Object 、 Set reg = CreateObject("VBScript.RegExp") 。这样写的坏处是失去了自动提示,好处是代码到哪里都能跑。我个人建议,写工具类的VBA代码,一律用后期绑定。特别是你想把工作簿发给同事用,或者你自己经常在不同电脑间切换,后期绑定能少一堆莫名其妙的兼容问题。

Sub CreateRegExp()
    ' 后期绑定创建正则对象
    Dim reg As Object
    Set reg = CreateObject("VBScript.RegExp")
    
    ' 设置匹配模式
    reg.Pattern = "\d+"      ' 匹配连续数字
    reg.Global = True        ' 全局匹配,而不是只匹配第一个
    reg.IgnoreCase = True    ' 忽略大小写
    
    ' 测试
End Sub

这里有一个新手常踩的坑: reg.Pattern 里面写反斜杠,比如 "\d+" ,在VBA字符串里不需要写成 "\\d+" ,因为VBA里的反斜杠没有转义作用。很多人从Python或者Java转过来,习惯性地双写反斜杠,结果正则匹配不到任何内容,还以为自己模式写错了。记住,VBA里 \d 就是 \d ,直接写。

2.2 五个属性和方法,覆盖所有正则操作

RegExp对象的完整接口不复杂,核心就三个属性加三个方法。

成员 类型 作用
Pattern 属性 设置或返回正则表达式模式
Global 属性 是否全局匹配。 False 时只匹配第一个结果, True 时匹配所有结果
IgnoreCase 属性 是否忽略大小写,默认 False
MultiLine 属性 是否把字符串视为多行,让 ^ 和 $ 匹配每行的开头和结尾
Test 方法 测试字符串是否匹配,返回 True 或 False
Execute 方法 执行匹配,返回 MatchCollection 集合
Replace 方法 按模式替换字符串,支持 $1 等分组引用

Test 适合做判断,比如“这个字符串里有没有手机号”; Execute 适合做提取,比如“把这段文字里所有手机号捞出来”; Replace 适合做清洗,比如“把日期格式统一成 2024-01-05 ”。这三个方法各管一摊,80%的文本处理需求都能覆盖。剩下20%的复杂场景,基本就是 Execute 提取后配合数组、字典做二次加工。

2.3 第一个能跑的例子:从一行文本里抓出所有手机号

写一个实际可运行的例子。假设A列是订单备注,B列开始输出提取到的手机号。手机号用最简单的规则:1开头,第二位是3到9,后面再跟9位数字。正则模式就是 1[3-9]\d{9} 。

Sub ExtractPhone()
    Dim reg As Object
    Dim matches As Object
    Dim m As Object
    Dim r As Range
    Dim outRow As Long
    Dim strText As String
    
    ' 正则对象放循环外,避免重复创建
    Set reg = CreateObject("VBScript.RegExp")
    reg.Pattern = "1[3-9]\d{9}"
    reg.Global = True
    
    outRow = 1
    For Each r In Range("A1:A" & Range("A" & Rows.Count).End(xlUp).Row)
        strText = r.Value
        If reg.Test(strText) Then
            Set matches = reg.Execute(strText)
            For Each m In matches
                Cells(outRow, 2).Value = m.Value
                outRow = outRow + 1
            Next m
        End If
    Next r
End Sub

这段代码里有两个关键细节。第一, Set matches = reg.Execute(strText) 执行一次能拿到这个单元格里所有匹配项,所以要用 For Each m In matches 去遍历, m.Value 就是匹配到的手机号字符串。第二, reg 对象必须在 For Each 循环外创建,如果每循环一个单元格就创建一次正则对象,四千行跑下来性能会差好几倍。这个习惯一旦养成了,后面处理大数据量会少踩很多坑。

跑完这个例子,你可能会在“立即窗口”用 Debug.Print reg.Replace("电话:13812341234", "[隐藏]") 测试替换效果,也可以试试把 Global 改成 False 看看匹配结果差异,你会发现 Global=False 时, Execute 只返回第一个手机号,这就是为什么我代码里习惯性把 Global=True 写上的原因。

3. VBA正则语法速查:这几张表够你应付八成场景

3.1 常用元字符和量词一览

这一节把最常用的正则语法过一遍。别被符号吓到,核心逻辑其实就三条: [] 表示“这一个位置可以是哪些字符”, 量词 表示“前面那个字符出现多少次”, () 表示“把这一段打包成一个整体”。理解了这三条,正则你就入门了。

语法 含义 示例
\d 匹配一位数字 \d\d\d 匹配三位数字
\w 匹配字母、数字、下划线,注意只匹配ASCII字符 \w+ 匹配一串单词字符
\s 匹配空白字符,包括空格、制表符、换行 \s+ 匹配连续空白
. 匹配除换行符外的任意字符 a.c 匹配 abc 、 a1c
^ 匹配字符串开头 ^\d 表示以数字开头
$ 匹配字符串结尾 \d$ 表示以数字结尾
* 前面的字符出现0次或多次 ab* 匹配 a 、 ab 、 abb
+ 前面的字符出现1次或多次 ab+ 匹配 ab 、 abb
? 前面的字符出现0次或1次 ab? 匹配 a 、 ab
{n} 前面的字符出现正好n次 \d{4} 匹配四位数字
{n,} 前面的字符出现至少n次 \d{2,} 匹配两位以上数字
{n,m} 前面的字符出现n到m次 \d{1,3} 匹配1到3位数字
[] 字符类,匹配其中任意一个字符 [abc] 匹配a、b、c
[^] 否定字符类 [^0-9] 匹配非数字
() 分组 (ab)+ 匹配 ab 、 abab
| 或 a|b 匹配a或b

一个新手特别容易困惑的地方: . 不匹配换行符。所以如果你在处理一段跨行的文本,想要“匹配任意字符”,不能用 .* ,要用 [\s\S]* 或者 [\s\S]+ 。 \s 匹配空白, \S 匹配非空白,两者合在一起就包含了所有字符。这个坑在抓取网页文本时特别常见,网页源码里到处都是 \r\n 换行,你写 <div>.*</div> 匹配不到,必须用 <div>[\s\S]*</div> 。

再补充一个中文匹配的关键点: \w 在VBScript正则里不会匹配中文。你写 \w+ 去匹配“张三李四”,结果是匹配不到的。要匹配中文,可以直接把起止字符写进字符类: [一-龥]+ 。这个范围的原理是中文字符在Unicode编码里是连续分布的,从 一 (U+4E00)到 龥 (U+9FA5)。这是VBA正则处理中文最实用的写法,建议直接背下来。

3.2 分组捕获和SubMatches:提取完整信息的钥匙

正则里的括号不仅是“打包”,还有一个重要能力叫“捕获分组”。比如要匹配“商品名称:白色T恤 价格:199元”里的名称和价格,可以写模式 商品名称:(.+?)\s+价格:(\d+元) ,括号里捕获到的内容会按顺序存进 SubMatches 集合, SubMatches(0) 对应第一个括号, SubMatches(1) 对应第二个括号。

看一个实际提取身份证号出生日期例子。身份证号是18位,中间的8位是出生日期,比如 110101199003077733 。

Sub ExtractBirthday()
    Dim reg As Object
    Dim matches As Object
    Dim m As Object
    Dim strText As String
    
    Set reg = CreateObject("VBScript.RegExp")
    reg.Pattern = "(\d{6})(\d{4})(\d{2})(\d{2})\d{4}"
    reg.Global = True
    
    strText = "客户身份证号110101199003077733,请核对"
    Set matches = reg.Execute(strText)
    
    For Each m In matches
        Debug.Print "出生年份:" & m.SubMatches(1)
        Debug.Print "出生月份:" & m.SubMatches(2)
        Debug.Print "出生日期:" & m.SubMatches(3)
    Next m
End Sub

m.SubMatches(1) 取到的就是出生年份分组 (\d{4}) 里的内容。注意 SubMatches 是一个从0开始索引的集合,第一个括号对应0,第二个对应1,跟数组的习惯一致。这个写法在处理“一条文本里夹着多个结构化信息”时特别高效,比你先用 Mid 数位置、再算偏移量要可靠得多。

3.3 VBScript正则的三个语法限制:避免“复制粘贴就报错”

如果你之前是在Python的 re 模块或者JavaScript里学的正则,转到VBA会碰到几个不支持的语法。这里集中说,免得你从网上复制一段现成正则过来直接报 运行时错误5017 。

第一,VBScript正则不支持字符串的 \uXXXX 写法。在Python里你可以写 [\u4e00-\u9fa5] 匹配中文,在VBA里这样写无效,必须直接写中文字符区间 [一-龥] 。同理, \p{L} 这种Unicode属性写法也不支持。

第二,VBScript正则不支持后行断言,也就是 (?<=...) 这种结构。向前断言 (?=...) 和负向前瞻 (?!...) 是支持的,但“匹配前面必须是某字符”的能力没有。替代方案通常是连上下文一起匹配出来,再用 SubMatches 或者 Replace 把多余部分去掉。比如想提取 价格:199元 里的数字,写成 价格:(\d+)元 ,取第一个分组,效果等价。

第三,VBScript正则不支持非捕获分组 (?:...) 和命名分组 (?<name>...) 。所有括号都会被当成捕获组参与编号。所以网上很多精细优化的正则,直接搬进来轻则不支持,重则理解错导致结果不对。我处理这类问题的方法是:拿到别人的正则先扫一遍有没有这三个语法,有就先简化重写,不要抱着侥幸心理测试。

4. 四个高频场景实战:提取、清洗、分类都能用正则

4.1 批量提取金额,带小数、带负号一次搞定

财务对账经常遇到“退款-12.50元”这种字样,要快速汇总金额。正则模式 -?\d+(\.\d+)? 可以覆盖“可选的负号、整数部分、可选的小数部分”。如果想连同千分位一起处理,比如“1,234.56”,模式改成 -?\d{1,3}(,\d{3})*(\.\d+)? 。

Sub ExtractAmount()
    Dim reg As Object
    Dim matches As Object
    Dim m As Object
    Dim r As Range
    Dim total As Double
    Dim i As Long
    
    Set reg = CreateObject("VBScript.RegExp")
    reg.Pattern = "-?\d+(\.\d+)?"
    reg.Global = True
    
    For Each r In Range("A1:A" & Range("A" & Rows.Count).End(xlUp).Row)
        Set matches = reg.Execute(r.Value)
        For Each m In matches
            ' 转为数值累加
            total = total + Val(m.Value)
            i = i + 1
        Next m
    Next r
    
    MsgBox "共提取到 " & i & " 笔金额,合计 " & total
End Sub

这里有一个隐藏细节: Val(m.Value) 会自动处理字符串开头可能带的正负号,但如果金额是负数且用了括号表示,比如 (12.50) , Val 函数不会把括号识别成负数,正则得预先处理。我一般先做一步 reg.Replace 把括号形式替换成负号形式,再跑提取。这类“先清洗、再提取、后计算”的三段式流程,是文本处理项目里的标准动作。

4.2 日期清洗:把各种不规整写法统一成标准格式

导出来的Excel日期经常五花八门: 2024.1.5 、 2024-01-05 、 2024年1月5日 、 2024/01/05 。如果直接用Excel的单元格格式去改,很多是文本字符串根本不认。用正则把它们统一成 2024-01-05 算是最经典的正则替换案例。

模式这样写: (\d{4})[年.\-/](\d{1,2})[月.\-/](\d{1,2})日? 。拆解一下:第一个分组捕获4位年份,接下来匹配“年、点、横杠、斜杠”任意一个分隔符,第二个分组捕获1到2位月份,再匹配“月、点、横杠、斜杠”任意一个分隔符,第三个分组捕获1到2位日期,最后的 日? 表示“日”字可以出现也可以不出现。

Sub NormalizeDate()
    Dim reg As Object
    Dim r As Range
    Dim result As String
    Dim y As Long, m As Long, d As Long
    
    Set reg = CreateObject("VBScript.RegExp")
    reg.Pattern = "(\d{4})[年.\-/](\d{1,2})[月.\-/](\d{1,2})日?"
    reg.Global = True
    
    For Each r In Range("A1:A" & Range("A" & Rows.Count).End(xlUp).Row)
        If reg.Test(r.Value) Then
            ' 从第一次匹配的分组里取出年月日
            y = CLng(reg.Execute(r.Value)(0).SubMatches(0))
            m = CLng(reg.Execute(r.Value)(0).SubMatches(1))
            d = CLng(reg.Execute(r.Value)(0).SubMatches(2))
            r.Value = Format(DateSerial(y, m, d), "yyyy-mm-dd")
        End If
    Next r
End Sub

这里用 DateSerial 函数组装日期,比手工拼接字符串补零要稳妥。1月会被 Format 自动补成 01 ,7日补成 07 。注意代码里我把 reg.Execute(r.Value)(0) 重复用了两遍,最理想的做法是先把匹配对象存进一个变量,减少重复调用,代码更流畅。我写这段是为了先讲清楚逻辑,实际项目里不会这样连写三遍 Execute 。

4.3 正则加字典:给备注文本打标签并快速分类统计

正则和VBA字典是黄金搭档。字典负责“键值对统计”,正则负责“从杂乱文本里识别标签”。比如一张工单表,备注里可能写着“客户要求加急”“订单备注:促销活动”“普通申请”等,你想统计各标签出现次数。

Sub CountTags()
    Dim reg As Object
    Dim matches As Object
    Dim m As Object
    Dim dict As Object
    Dim r As Range
    
    Set reg = CreateObject("VBScript.RegExp")
    reg.Pattern = "紧急|加急|普通|促销|退款"
    reg.Global = True
    
    Set dict = CreateObject("Scripting.Dictionary")
    
    For Each r In Range("A1:A" & Range("A" & Rows.Count).End(xlUp).Row)
        If reg.Test(r.Value) Then
            Set matches = reg.Execute(r.Value)
            For Each m In matches
                If dict.Exists(m.Value) Then
                    dict(m.Value) = dict(m.Value) + 1
                Else
                    dict.Add m.Value, 1
                End If
            Next m
        End If
    Next r
    
    ' 输出统计结果到D列
    Dim i As Long
    Dim key As Variant
    i = 1
    For Each key In dict.Keys
        Cells(i, 4).Value = key
        Cells(i, 5).Value = dict(key)
        i = i + 1
    Next key
End Sub

这段代码最大的价值在于,词典的 Keys() 天然去重,统计结果直接写回单元格,比用 CountIf 一列一列去数不知道快多少。而且正则模式里的 | 表示“或”,你可以随时加新标签,比如加一个“投诉”,正则改成 紧急|加急|普通|促销|退款|投诉 就行。维护成本几乎为零,这在Excel函数方案里很难做到。

4.4 网页抓取数据后的文本清理:去标签、提取链接

很多朋友做网页数据下载时,把HTML源码直接粘进了单元格,里面全是 <div> 、 <span> 、 class="..." 这些标签。正则清理HTML是这个场景的看家本领。

最简单的清理HTML标签,模式就是 <[^>]+> 。意思是“一个小于号、后面跟一个或多个非大于号字符、最后加一个大于号”。 Replace 成空字符串,标签全没了。注意 [^>] 这个写法很关键,它保证了匹配到的是“最短的”标签,不会把整个HTML大段误删。

提取链接是另一个高频需求。假设网页源码里有 <a href="http://example.com/page" title="某页面"> ,想提取href属性里的网址,模式写 href="([^"]*)" 。取 SubMatches(0) 就是链接地址。这里 [^"]* 的意思是“在双引号里面匹配任意多个非双引号字符”,这样链接里即使有空格也能完整捕获。

需要提醒的是,正则处理HTML只适用于简单、可控的页面结构。如果你要解析结构复杂、嵌套严重、属性顺序不固定的HTML,用正则硬写会非常痛苦,不如用XMLHTTP请求后换专业的解析工具,或者调用HTMLDocument对象去遍历DOM。VBA环境里有现成的 MSHTML 库可以解析HTML文档对象,那才是重型场景的正道。正则适合“快速清洗文本、抓取字段”,不必死磕到底。

5. 正则排查清单:报错、匹配不到、跑得慢都能快速定位

5.1 常见报错和结果不对的原因速查

现象 常见原因 解决办法
运行时错误5017 正则语法错误 检查括号是否配对、 [ 是否闭合、量词是否放到无意义位置
明明有内容却匹配不到 模式里用了 \u 或其他不支持的语法 改成 [一-龥] 这种VBA支持的写法
只匹配到第一个结果 Global 是 False 设置 reg.Global = True
替换结果把整段文字都删了 贪婪匹配导致匹配范围过大 用 .*? 或 [^>]* 这类非贪婪写法
从网页文本提取总是失败 文本里有换行, . 不匹配换行 用 [\s\S]* 替代 .*
中文匹配不到 \w 和 \d 不覆盖中文 直接用 [一-龥] 范围匹配
Replace之后有 $1 没被替换 分组数量对不上 确认模式里的括号数量和 $1 的编号对应

关于贪婪匹配,多说两句。模式 <.*> 在匹配 <a>123</a> 时,因为 .* 会尽量吃更多字符,所以匹配到的结果是 <a>123</a> 整个字符串,而不是 <a> 。想让匹配停下来,要么用 [^>]* 限定“只能是非大于号字符”,要么用 *? 推进非贪婪匹配。两者在VBA正则里都有效,我实际项目中用 [^>]* 更多,因为意图更明确,避免某些场景下非贪婪的边界行为跟预想不一致。

5.2 宏被禁用、加载项被禁用的处理办法

正则写得再好,如果文件打开就提示“宏已被禁用”,代码根本跑不起来。Excel默认的安全策略是禁用所有宏,所以你需要把文件另存为 xlsm 格式,然后去“文件→选项→信任中心→信任中心设置→宏设置”里选择“启用所有宏”。自己写的工具,启用宏是没问题的,但要留意不要随意运行来路不明的工作簿宏。

如果你在Excel里遇到“加载项被禁用”的提示,多半是某个COM加载项启动时报错,被Excel自动禁用了。处理路径是:“文件→选项→加载项→管理COM加载项→转到”,在列表里重新勾选被禁用的项并确定。有时候加载项之间还会打架,表现为快捷键失灵,比如Ctrl+C和Ctrl+V用不了,或者工具栏选项消失。我遇到过装了一个PDF导入工具后,整个Excel的快捷键全失效的情况,禁用那个加载项后恢复正常。这类问题跟代码本身无关,但排查的时候容易耽误时间,知道路径能少走弯路。

还有一个小细节:修改VBA代码后,如果Excel提示“是否保存对VBA工程所做更改”,记得保存,并且该文件必须是 xlsm 格式才能保留宏代码。如果你另存为 xlsx ,代码会静默丢失,下次打开只剩数据。这个坑很多人碰到过一次就再也不会忘。

5.3 几千行数据跑得慢的优化技巧

正则本身执行速度是很快的,感觉慢通常是VBA的瓶颈。最常见的问题是在循环里反复操作单元格,频繁读写工作表。优化思路有三个。

第一,正则对象创建一次,不要放在循环里。一个 CreateObject 虽然开销不大,但几千行每行都创建一次,慢得肉眼可见。第二,把数据一次性读入数组,在内存里用正则处理完再批量写回。数组操作比单元格逐行读写快一个数量级,这个从我们前面聊VBA数组那章就强调过。第三,处理前保存 Application.ScreenUpdating = False 和 Application.Calculation = xlCalculationManual ,处理完在 Finally 或结尾恢复,界面不刷新、公式不重算,速度提升非常明显。

更要警惕的是“灾难性回溯”。如果你写的正则模式里出现了嵌套的重复,比如 (.*)* 、 (\d+)+ 这样的组合,当匹配失败时,引擎会尝试指数级的回溯路径,小数据感觉不到,几千行文本可能直接卡死。遇到模式复杂、跑起来特别慢的情况,优先简化量词嵌套,把多个 * 、 + 压缩成单一的 [\s\S]* 或 [^>]* ,通常能救回来。

我在实际项目中处理文本类VBA任务,已经习惯了“先写一小段测试代码在立即窗口里验证正则模式,确认匹配结果无误,再套进正式数据处理循环”的工作方式。测正则时用最短的代表性字符串,跑通了再上完整数据,一次报错排查成本远低于在几千行数据里反复试错。这个小习惯,对我个人来说帮了大忙。

正则表达式这套东西,初次接触会觉得满屏幕符号像乱码,但一旦你习惯了“用结构描述文本”的思路,再回头用Excel的手工操作和公式拼接,就会有一种明显的降维打击感。很多时候我处理一张乱糟糟的导入表,前后不超二十分钟,一半时间花在理解字段逻辑上,另一半就是写正则加跑测试,真正到了清洗环节反而是最轻松的。下一章我准备聊聊VBA读写文件,其实文件解析、日志分析这些场景,正则依然是绝对主力。

更多推荐