VBA正则表达式中引入贪婪匹配与惰性匹配两种模式,本质上是为编程者提供控制匹配“粒度” 的关键工具。因此,理解并正确运用这两种模式,能够有效解决文本处理中常见的边界模糊问题,快速实现精准数据提取和文本分析
贪婪匹配
上一期我们提到的分组+量词组合使用,默认就是贪婪匹配。贪婪匹配的特点是倾向于获得最长的可能匹配子串
举个例子:从混合文本“<title>学习VBA</title><title>正则表达式</title>”中提取<title>和</title> 之间的所有内容,即“学习VBA”和“正则表达式”两处
以下是使用分组+量词的默认组合方式实现,看看能否实现诉求
Dim match' 创建正则表达式对象With CreateObject("VBScript.RegExp").Global = True ' 搜索全部匹配项.IgnoreCase = False ' 区分大小写.Pattern = "<title>(.*)</title>"For Each match In .Execute("<title>学习VBA</title><title>正则表达式</title>")Debug.Print match.SubMatches(0)NextEnd With
可惜,代码执行后的结果与预期存在差距
通过设置断点,再次执行,查看match变量,我们可以清晰看到获取的内容
这是因为模式匹配 <title>(.*)</title> 中,(.*) 使用了通配符 . 和量词 *。在默认情况下,量词 * 是贪婪的,这意味着它会尽可能多地匹配字符
当应用于字符串“ <title>学习VBA</title><title>正则表达式</title>” 时,正则引擎的匹配过程如下:
首先找到第一个 <title>
接着,(.*) 开始匹配。由于是贪婪模式,它会一直向后匹配,直到字符串的末尾
然后,引擎开始回溯,尝试满足模式的后半部分 </title>。它会在字符串末尾找到最后一个 </title>,并成功完成一次匹配
虽然.Global = True,理论上会搜索所有匹配项,但在这个例子中,第一次贪婪匹配已经消耗了从第一个 <title> 到最后一个 </title> 之间的所有字符,没有剩余的文本可供第二次匹配,所以循环只会执行一次
最终,整个匹配结果:从第一个 <title> 开始,到最后一个 </title> 结束的最长可能字符串,即“ 学习VBA</title><title>正则表达式”。这就是 match.SubMatches(0) 获取到的内容,与代码执行后的结果一致
惰性匹配
既然通过贪婪匹配模式失败了,就要再次探讨寻求另一种方式——惰性匹配,也称为“非贪婪匹配”
贪婪量词后面追加一个问号 ? 即可启用惰性匹配,代码如下:
Dim match' 创建正则表达式对象With CreateObject("VBScript.RegExp").Global = True ' 搜索全部匹配项.IgnoreCase = False ' 区分大小写.Pattern = "<title>(.*?)</title>"For Each match In .Execute("<title>学习VBA</title><title>正则表达式</title>")Debug.Print match.SubMatches(0)NextEnd With
执行后结果:
在这种模式下,量词会尽可能少地匹配字符,一旦满足后续的匹配条件就立即停止,修改后,匹配过程变为:
找到第一个 <title>
(.*?) 开始非贪婪匹配,它会在遇到第一个满足条件的 </title> 时就立即停止。因此,它只匹配了“学习VBA”。
完成第一次匹配,得到 match.SubMatches(0) 为“学习VBA”
由于设置了 .Global = True,正则引擎会从上次匹配结束的位置之后继续搜索
找到第二个 <title>,(.*?) 匹配到其后的第一个 </title>,得到“正则表达式”
最后,循环结束,成功获得两个独立结果,是不是很有趣呢?
结束语
两段代码仅仅只差一个问号(?),结果却天差地别,这也仅仅才是正则表达式的一个语法。因此,很多小伙伴反馈正则表达式难学,大多数原因应该都在于这些细节上吧,毕竟一个符号决定胜负!
好了,今天的分享就到这里了,咱们下期继续
公众号同时也在不间断地分享免费的编程案例,如果想学习更多的编程知识,无论是用来提升自动化办公效率还是想提升自我,都可以关注我的公众号“努力鸭是黑色的”,解锁更多的VBA技能