在C#中,可以很容易地将一个拆分字符串拆分成多个字符,一个提供了一个拆分字符数组。我想知道在VBA中实现此目标的最佳方法是什么。我通常使用VBA.Split,但要分割多个字符,需要钻取结果并再分割元素。然后必须重新排列数组等的尺寸。非常痛苦。
约束
仅VBA回应。如果需要,可以使用.NET集合类(yes they are creatable and callable in VBA)。如果需要,可以使用JSON,XML作为分割段列表的容器。如果愿意,可以使用不起眼的VBA.Collection类,甚至可以使用Scripting.Dictionary。如果愿意,您甚至可以使用伪造的记录集。
我完全知道可以编写一个.NET程序集来调用.NET String.Split方法,并通过COM接口将程序集公开给VBA,但这是挑战所在。
答案 0 :(得分:3)
在尝试分割空格之前,我用空格替换了所有其他字符。 (所以我有点作弊。)
Private Function SplitByMoreThanOneChars(ByVal sLine As String)
'*
'* Brought to you by the Excel Development Platform Blog
'* http://exceldevelopmentplatform.blogspot.com/2018/11/
'*
'* Don't get excited, this splits by spaces only
'* we fake splitting by multiple characters by replacing those characters
'* with spaces
'*
Dim vChars2 As Variant
vChars2 = Array(" ", "<", ">", "[", "]", "(", ")", ";")
Dim sLine2 As String
sLine2 = sLine
Dim lCharLoop As Long
For lCharLoop = LBound(vChars2) To UBound(vChars2)
Debug.Assert Len(vChars2(lCharLoop)) = 1
sLine2 = VBA.Replace(sLine2, vChars2(lCharLoop), " ")
Next
SplitByMoreThanOneChars = VBA.Split(sLine2)
End Function
答案 1 :(得分:3)
使用正则表达式应该相当容易。如果您对传递的字符取反进行拆分,则这些匹配将成为输出数组的成员。这样做的好处是输出数组仅需要调整一次大小,因为您可以获得RegExp
返回的匹配计数。该模式的构建非常简单-可以归结为类似[^abc]+
的样式,其中'a','b'和'c'是要分割的字符。准备表达式所需要做的唯一一件事就是在正则表达式中转义几个在该上下文中具有特殊含义的字符(我可能忘记了一些):
Private Function BuildRegexPattern(ByVal inputString As String) As String
Dim escapeTargets() As String
escapeTargets = VBA.Split("- ^ \ ]")
Dim returnValue As String
returnValue = inputString
Dim idx As Long
For idx = LBound(escapeTargets) To UBound(escapeTargets)
returnValue = Replace$(returnValue, escapeTargets(idx), "\" & escapeTargets(idx))
Next
BuildRegexPattern = "[^" & returnValue & "]+"
End Function
一旦有了模式,只需简单地确定数组的大小并迭代匹配项以分配它们(加上一些其他特殊情况处理等)即可。
Public Function MultiSplit(ByVal toSplit As String, Optional ByVal delimiters As String = " ") As String()
Dim returnValue() As String
If toSplit = vbNullString Then
returnValue = VBA.Split(vbNullString)
Else
With New RegExp
.Pattern = BuildRegexPattern(IIf(delimiters = vbNullString, " ", delimiters))
.MultiLine = True
.Global = True
If Not .Test(toSplit) Then
'Only delimiters.
ReDim returnValue(Len(toSplit) - 1)
Else
Dim matches As Object
Set matches = .Execute(toSplit)
ReDim returnValue(matches.Count - 1)
Dim idx As Long
For idx = LBound(returnValue) To UBound(returnValue)
returnValue(idx) = matches(idx)
Next
End If
End With
End If
MultiSplit = returnValue
End Function