This is an old question and the OP has probably moved on. However, when I looked for something on this problem, this was the page I found. In case others find it, here is my attempt at a better answer and a link to an even better answer.
I cannot find an adequate explanation of the problem in either the question or any of the previous answers. In my experiments, the accepted answer performs worse than the inbuilt method of building a long string.
The problem
Suppose I write:
StrVar = "abcde"
StrVar = StrVar & "fghij"
StrVar = StrVar & "klmno"
For the first statement, the VBA interpreter finds memory sufficient to hold 5 characters, writes "abcde" to that memory and points StrVar at that memory.
For the second statement, the VBA interpreter finds memory sufficient to hold 10 characters, copies the existing contents of StrVar to that memory, appends "fghij", points StrVar at that memory and releases the old memory for garbage collection.
For the third statement, the VBA interpreter find memory sufficient to hold 15 characters, copies the existing contents of StrVar to that memory, appends "klmno", points StrVar at that memory and releases the old memory for garbage collection.
The point is VBA strings are immutable; they are never changed. Instead, every time your macro amends a string, new memory is found for the amended string and the memory for the original string is released. For a few string amendments, nothing is done about the released memory so there is no obvious time wasted building a long string. But, if you do this often enough, the interpreter runs out of new memory and it has to call the garbage collector. The garbage collector works through all the memory identifying what bits are still being used and which bits aren’t. It moves all the bits that are still being used together leaving the unused bits as new, available memory. Control is returned to the interpreter which continues handling new string assignments until it runs out of new memory again.
If you are building a longer and longer string, the interpreter runs out of memory faster and faster until your macro slows to a crawl.
What I wanted to achieve, my solution and a faster solution
I wanted to convert an Excel range to Html. Ignoring formatting, I wanted to create a string like this:
<table>
<tr>
<td> … </td>
<td> … </td>
</tr>
<tr>
<td> … </td>
<td> … </td>
</tr>
<tr>
<td> … </td>
<td> … </td>
</tr>
</table>
On my laptop, the duration of macro building this string increased steadily until about 10,000 cells. It then started slowing down significantly:
Duration
in secs Cells
0.000 200
0.016 400
0.031 1,000
0.062 2,000
0.14 4,000
0.44 8,000
0.67 10,000
0.97 12,000
2.27 14,000
6.79 16,000
10.9 18,000
63 32,000
On my laptop, 8,000 cells took 3 times as long as 4,000 cells; 16,000 cells took 15 times as long as 8,000 cells and 32,000 cells took 9 times as long as 16,000 cells. If you repeat my experiment, the increase in duration on your computer will depend on how much memory you have and on whatever else you are doing.
I have no expectation of needing to convert 32,000 cells. However, for this experiment, I was only converting the cell value. Once I started converting the formatting, string lengths would increase dramatically and the maximum number of cells with acceptable performance would drop,
My next idea was to create a string array with one element per row. Output each table row to a different element of the string array and then Join
the elements to create the full string. This is the same idea as in the accepted answer. When I tried this idea, 32,000 cells took 91 seconds. Perhaps I could have reduced the duration by optimising my code but this did not appear to be a promising approach.
My next idea was to write each bit of the string to a disc file and then read the file to get the entire. I have not timed them properly but disc reads and write have always seems fast enough. With this approach, durations were:
Duration
in secs Cells
.375 10,000
.609 18,000
3.45 100,000
34.8 1,000,000
These durations are much more acceptable. The duration will increase as I start converting the formatting but since 1,000,000 cells is way more that I expect to convert, I should get acceptable performance.
To show my technique, this is the important part of my code:
' Needs reference to Microsoft Scripting RunTime
Dim Body As String
Dim ColCrnt As Long, ColLeft As Long, ColRight As Long
Dim FileOut As TextStream
Dim Fso As FileSystemObject
Dim PathFileName As String
Dim RowBot As Long, RowCrnt As Long, RowTop As Long
Dim Wsht As Excel.Worksheet
Set Fso = CreateObject("Scripting.FileSystemObject")
PathFileName = ThisWorkbook.Path & "\RtH.txt"
' Create text file. First True = Overwrite existing file. Second True = Unicode output
Set FileOut = Fso.CreateTextFile(PathFileName, True, True)
FileOut.WriteLine "<table>"
For RowCrnt = RowTop To RowBot
FileOut.WriteLine " <tr>"
For ColCrnt = ColLeft To ColRight
FileOut.WriteLine " <td>" & Wsht.Cells(RowCrnt, ColCrnt).Value & "</td>"
Next
FileOut.WriteLine " </tr>"
Next
FileOut.WriteLine "</table>"
FileOut.Close
' Open text file. 1 = Open for reading. -1 = Unicode
Set FileOut = Fso.OpenTextFile(PathFileName, 1, -1)
Body = FileOut.ReadAll
FileOut.Close
Having got this far in this answer, I had another look for a solution to the string building problem and discovered: A lightning-fast StringBuilder
This page contains several version of the solution as different people suggested improvements. I tried the version by Duran. The duration of my code to convert 1,000,000 cells reduced from 34.8 seconds with my disc solution to 3.92 seconds.
My solution has the advantage that it only uses simple, easy VBA and is way faster than any other simple solution. I have tested it with a variety of characters across the 16-bit Unicode range without problems.
The linked solution used more advanced VBA. I would not let that stop you from using it; you use VBA standard routines without knowing how they work. I suggest reading every version of the solution and every comment since there is much useful information. I chose the Duran version but you might prefer another. The only important point that I could not find in any of the answers or comments is that the code must be placed in a Class module named “StringBuilder”.