'预查/断言/环视
Sub 断言()
'前瞻断言
' `(?=...)` - 正向前瞻断言
' =(*pla:...)
' =(*positive_lookahead:...)
' `(?!...)` - 负向前瞻断言
' =(*nla:...)
' =(*negative_lookahead:...)
reg.Pattern = "\w+(?=123)"
Debug.Print reg.Execute("password123")(0).Value '匹配password
reg.Pattern = "\w+(?!123)"
Debug.Print reg.Execute("password456")(0).Value '匹配password456
'后顾断言
' `(?<=...)` - 正向后顾断言(固定长度)
' =(*plb:...)
' =(*positive_lookbehind:...)
' `(?<!...)` - 负向后顾断言(固定长度)
' =(*nlb:...)
' =(*negative_lookbehind:...)
reg.Pattern = "(?<=:)\d+"
Debug.Print reg.Execute("price:100")(0).Value '匹配100
reg.Pattern = "(?<!:)\d+"
Debug.Print reg.Execute("amount200")(0).Value '匹配200
'回溯控制语法
'以下指令到达时立即触发
'(*ACCEPT) 强制匹配成功
'(*FAIL) = (*F) 强制回滚
'(*MARK:NAME) = (*:NAME) 设置名称以传递回来
'仅当后续匹配失败导致回溯到达他们时,才会执行以下操作。
'他们都强制匹配失败,但之后会发生什么却各不相同。
'那些提前匹配开始的只有在模式没有锚定的情况下才会这样做
'(*COMMIT) 整体失败,起点没有前进
'(*PRUNE) 前进到下一个起始字符
'(*SKIP) 前进到当前匹配位置
'(*SKIP:NAME) 前进到与较早位置对应的位置
' 未找到(*MARK:NAME); 则忽略(*SKIP)
'(*THEN) 局部故障,跳到下一个替代项
'非原子正向前瞻
' (?* 或 (*napla: 或 (*non_atomic_positive_lookahead:
' 上述语法我没搞懂
' 类似模拟:(?>(?=pattern))
' 首先尝试匹配 pattern
' 如果匹配成功,不允许回溯到 pattern 中去尝试其他可能的匹配
' 在匹配成功后,断言位置保持不变(正向前瞻的特性)
'示例 1:匹配数字前面的字母,但不回溯
reg.Pattern = "(?>(?=[a-z]\d))([a-z])" '匹配数字前面的一个字母,不回溯
Set mc = reg.Execute("aa1bb2cc3dd4")
For i = 1 To mc.Count
Debug.Print mc(i - 1).Value
Next i
'示例 2:匹配字符串 "a" 或 "ab",但在遇到 "ab" 时不回溯到 "a"
reg.Pattern = "(?>(?=ab))a|b"
'案例 1:(?*<p>.*?</p>)\K.*?(?=</p>)
'目标:提取 <p>...</p> 标签内的纯文本(不包括标签本身),但要求不匹配嵌套的 <script> 标签内容
'(?*<p>.*?</p>):向前看,确保当前有 <p> 开头并匹配到最近的 </p>。这里使用了 .*?(非贪婪匹配)。如果 <p> 标签内有 <script>,.*? 会尝试匹配 <script>,但由于后面的正则(\K.*?(?=</p>))要求匹配纯文本,它会回溯尝试让 .*? 匹配更少的字符,最终只匹配到 <script> 开始前的文本
'\K:重置匹配起点,只返回后面的内容
'.*?(?=</p>):匹配直到 </p> 前的内容
'案例 2:日期格式匹配(灵活分隔符)
'目标:匹配形如 2024-01-01 或 2024/01/01 的日期,但要求分隔符前后必须一致(不能出现 2024-01/01)
'(?* \d{4}([-\/])\d{2}\1\d{2} )
'(?* \d{4}([-\/])\d{2}\1\d{2} ):正向前瞻内部包含 \1(反向引用),指向前面捕获的分隔符。如果输入是 2024-01/01,\1 期待 -,但实际是 /,导致前瞻不匹配
'关键点:这里使用非原子性断言是为了让引擎在遇到 - 或 / 后仍然继续尝试匹配后面的 \1,而不是因为分隔符不匹配就立即终止
'非原子负向前瞻
' (?<* 或 (*naplb: 或 (*non_atomic_positive_lookbehind:
' 上述语法我没搞懂
' 类似模拟:(?>(?!pattern))
' 首先尝试匹配 pattern。
' 如果 pattern 不匹配,则匹配成功。
' 如果 pattern 匹配成功,则匹配失败,并且不允许回溯到 pattern 中去尝试其他可能的匹配
'(?>(?!ab))\w+ '匹配非 "ab" 的字符串
'(?>(?!test_))[a-zA-Z_]\w* '匹配所有不以 test_ 开头的变量名
'案例 1:提取不在引号内部的单词
'(?<*'[^']*)\bword\b
'(?<*'[^']*):负向后顾,检查当前位置前面是否有未闭合的单引号 '
'如果前面有 ' 开始但没有对应的结束 '(即仍在引号内部),[^']* 会匹配到字符串末尾。这时后面的 \bword\b 就会失败
'关键点:如果 word 位于引号内部,后顾内部的 [^']* 会尝试匹配(包括 word 本身),但由于整体匹配失败,后顾内部会回溯尝试(例如匹配更少字符),最终发现当前位置是在引号内部而失败,从而不匹配
'案例 2:前缀匹配(不定长)
'目标:匹配字符串中 以 AB 开头 并且 后面紧跟至少一个数字 的子串
'(?<*AB\d+).*
End SubCopyright © 2025- vba.vip All Rights Reserved.