I read that someone uses a string builder class in vba. For some reason, that thought had never occurred to me, but I thought I’d give it a try. Here’s my first attempt.
Public Property Get Text() As String
Text = msText
End Property
Public Sub Append(sText As String, Optional sDelim As String = vbNewLine)
msText = msText & sText & sDelim
End Sub
Public Sub AppendFormat(vNumber As Variant, sFormat As String, _
Optional sDelim As String = vbNewLine)
msText = msText & Format(vNumber, sFormat) & sDelim
End Sub
Public Sub Insert(sText As String, lPosition As Long)
msText = Left$(msText, lPosition – 1) & sText & Mid$(msText, lPosition, Len(msText))
End Sub
Public Sub StrReplace(sOldText As String, sNewText As String, _
Optional lStart As Long = 1, Optional lHowMany As Long = -1)
msText = Replace(msText, sOldText, sNewText, lStart, lHowMany)
End Sub
Public Sub PrintImmediate()
Debug.Print msText
End Sub
Public Sub Message()
MsgBox msText, vbOKOnly
End Sub
Public Property Get Reverse()
Dim i As Long
Dim sReturn As String
For i = Len(msText) To 1 Step -1
sReturn = sReturn & Mid(msText, i, 1)
Next i
Reverse = sReturn
End Property
And to use it
Dim sb As CStringBuilder
Set sb = New CStringBuilder
sb.Append “The quick brown”, ” “
sb.Append “fox jumped over”, ” “
sb.Append “the lazy dog.”
sb.PrintImmediate
sb.StrReplace “lazy”, “stupid”
sb.PrintImmediate
sb.AppendFormat 14.5, “$#,##0.00”
sb.Message
Debug.Print sb.Reverse
End Sub
Do you use a string builder class? What properties and methods am I missing?
Dick –
Why roll your own?
Reverse = StrReverse(mstext)
End Property
…mrt
One reason why people roll their own string building class in VBA is to get around the performance hit imposed by regular string concatenation. Each concatenation requires a string copy, so building a string by concatenation means repeatedly copying most of it. What should be a time proportional to the string length ends up taking a time proportional to the *square* of the length.
You can make a very simple “string appender” class by just stuffing the individual strings into a Collection, and then copying them into an array and using Join() to get back your built-up string. (I’ve never tested to see how Join() performs, though.)
Here is a nifty example of one that uses a string buffer instead:
http://stackoverflow.com/questions/1070863/hidden-features-of-vba/1338833#1338833
You may have read my comment over on Smurf’s blog. My implementation is much more simplistic and was driven mainly by the need to track variables through a complicated process and spit out the results at the end, and my hate for constantly typing “& vbCrLf”.
Here is my CStringBuilderItem:
Public Property Get ItemText() As String
ItemText = m_ItemText
End Property
Public Property Let ItemText(ByVal NewText As String)
m_ItemText = NewText
End Property
Here is my CStringBuilder Class:
Public Sub AddText(NewText As String, Optional sKey As String)
Dim oSBI As CStringBuilderItem
Set oSBI = New CStringBuilderItem
oSBI.ItemText = NewText
If Len(sKey) = 0 Then
mCol.Add oSBI
Else
mCol.Add oSBI, sKey
End If
Set oSBI = Nothing
End Sub
Public Function ToString(Optional ByVal AddNewLine As Boolean = True) As String
Dim returnValue As String
Dim delimiter As String
Dim i As Integer
If mCol.Count = 0 Then
returnValue = “”
Else
returnValue = mCol.Item(1).ItemText
If mCol.Count > 1 Then
If AddNewLine Then
delimiter = vbCrLf
Else
delimiter = ” “
End If
For i = 2 To mCol.Count
returnValue = returnValue & delimiter & mCol.Item(i).ItemText
Next
End If
End If
ToString = returnValue
End Function
Public Sub ClearAll()
Dim i As Integer
Dim ii As Integer
ii = mCol.Count
For i = ii To 1 Step -1
mCol.Remove i
Next
End Sub
Public Property Get Item(vntIndexKey As Variant) As CStringBuilderItem
Set Item = mCol(vntIndexKey)
End Property
Public Property Get Count() As Long
Count = mCol.Count
End Property
Public Sub Remove(vntIndexKey As Variant)
mCol.Remove vntIndexKey
End Sub
Public Property Get NewEnum() As IUnknown
Set NewEnum = mCol.[_NewEnum]
End Property
Private Sub Class_Initialize()
Set mCol = New Collection
End Sub
Private Sub Class_Terminate()
Set mCol = Nothing
End Sub
Again, it’s nothing special, but it saves me time when writing debugging tracers.
Oh, and I forgot to add, VBSpeed has some pretty good stuff for people who want to go waaay overboard in string handling optimization for VB/VBA:
http://www.xbeat.net/vbspeed/
If you’re going to make an unnecessary text string class, why wouldn’t you also make an unnecessary number class which including number format for string representation as one of its properties?
VBA doesn’t provide overloading, so if syntax checking is wanted, methods and properties can accept one and only one type for each argument. The alternative of passing arguments as variants means no SYNTAX checking, but the need to put RUNTIME type checking code into methods and properties and throw RUNTIME errors when they’re passed anything invalid.
VBA’s object-oriented facilities are half-baked. Best avoided when possible. A text string class as a wrapper around a plain String variable is a fine example of an unnecessary and inefficient class.
There are various links in the comments to this posting:
http://roymacleanvba.wordpress.com/2010/01/14/performance-strings/
regards
/Roy
Hi folks, here’s my small but very efficient class:
Private m_strDelimiter
Private Sub Class_Initialize()
m_arrBuffer = Array()
m_strDelimiter = “”
End Sub
Private Sub Class_Terminate()
m_arrBuffer = Empty
End Sub
Public Property Get Delimiter()
Delimiter = m_strDelimiter
End Property
Public Property Let Delimiter(strDelimiter)
m_strDelimiter = strDelimiter
End Property
Public Sub Append(strValue)
ReDim Preserve m_arrBuffer(UBound(m_arrBuffer) + 1)
m_arrBuffer(UBound(m_arrBuffer)) = strValue
End Sub
Public Sub AppendLine(strValue)
Me.Append strValue & vbCrLf
End Sub
Public Sub Compact()
If Not Me.Delimiter = “” Then
strOriginalDelimiter = Me.Delimiter
Me.Delimiter = “”
End If
strTemp = Me.ToString
m_arrBuffer = Array()
Me.Append strTemp
Me.Delimiter = strOriginalDelimiter
End Sub
Public Function ToArray()
ToArray = m_arrBuffer
End Function
Public Function ToString()
ToString = Join(m_arrBuffer, m_strDelimiter)
End Function