实例需求:数据序列使用减号连接,其中序列中包含重复元素。需要提取非重复元素,并使用减号连接,对于重复元素保留最后一次出现位置。
测试字符串:A-B-D-CC-C-C-F-C-E-C-D-E-CC-F
重复元素标记为蓝色,去重后结果为:A-B-C-D-E-CC-F
这个字符提取规则相对简单,直接使用VBA方法也可以实现。
Sub VBA_DEMO_0725()
Dim arrData
Dim i As Integer
Dim dic As Object
Dim n As Integer
Dim arrRes()
Dim arrTmp()
Set dic = CreateObject("scripting.dictionary")
strTxt = "A-B-D-CC-C-C-F-C-E-C-D-E-CC-F"
arrData = Split(strTxt, "-")
For i = UBound(arrData) To LBound(arrData) Step -1
If Not dic.exists(arrData(i)) Then
dic(arrData(i)) = arrData(i)
End If
Next i
arrTmp = dic.Items
n = 1
For i = dic.Count - 1 To 0 Step -1
ReDim Preserve arrRes(1 To n)
arrRes(n) = arrTmp(i)
n = n + 1
Next
Debug.Print Join(arrRes, "-")
Set dic = Nothing
End Sub
【代码解析】
提取唯一值最简单的实现方式就是字典对象,第8行代码创建字典对象。
第10行代码就字符串拆分为数组。
对于重复元素保留最后一次出现位置,所以第11行到第15行倒序读取数组,加入到字典对象中。
第16行代码将字典对象的值保存在数组中。
第18行到第22行将唯一值数组倒序保存在结果数组中。
第23行代码使用Join函数
组合唯一值结果为字符串。
使用正则来实现这个需求代码会更简单。
Sub RegExpDemo()
Dim strTxt As String
Dim objRegEx As Object
Set objRegEx = CreateObject("vbscript.regexp")
objRegEx.Pattern = "\b(\w+)-(?=.*-\1(-|$))"
objRegEx.Global = True
strTxt = "A-B-D-CC-C-C-F-C-E-C-D-E-CC-F"
If objRegEx.test(strTxt) Then MsgBox objRegEx.Replace(strTxt, "")
Set objRegEx = Nothing
End Sub
【代码解析】
第5行代码设置正则匹配模式,其具体含义如下。
正则表达式 | 含义 |
---|---|
\b | 匹配英文单词的边界,所在位置的一侧为单词字符,另一侧为非单词字符、字符串的开始或结束位置 |
(\w+) | 匹配一个或者多个英文字符,相当于[a-zA-Z] |
- | 匹配分隔符 |
(?=.*-\1(-|$)) | 是一个零宽正向先行断言,\1 提取第一个匹配组,代表重复出现的元素,重复元素之前为分隔符,其后可以是分隔符或者字符串结束位置,在两个重复元素之间可以包含任意多个字符,此处使用.* 为贪婪匹配 |
正则可以成功匹配七次,即如下的蓝色粗体部分字符(不包括分隔符)
A-B-D-CC-C-C-F-C-E-C-D-E-CC-F
第8行代码使用正则替换,将匹配部分替换为空。
粗略来看,构建正则表达式时的思路有两大类:
- 利用匹配组,获取匹配结果
- 匹配不需要的字符,利用正则替换为空,进而获得所需结果。
相关博文链接:
VBA之正则表达式(12)-- 格式调整
VBA之正则表达式(13)-- 字符串变换
VBA之正则表达式(14)-- 提取指定位数的数字
VBA之正则表达式(15)-- 提取数字求和
VBA之正则表达式(16)-- 提取非重复值
VBA之正则表达式(17)-- 提取多组数据(去除末尾字符)