Fun With PowerShell – StringBuilder

Introduction

As I was creating the next post in my ArcaneBooks series, I realized I had not written about the StringBuilder class. As the code in my ArcaneBooks module relies on it in several places, I thought it best to add a new post to my Fun With PowerShell series explaining how to use it before continuing.

It’s a common need in any language, and PowerShell is no exception, to need to add more text to an existing string.

What many people don’t realize though is that PowerShell strings are immutable. They cannot change. As an example, let’s talk about what happens behind the scenes when you execute this code sample.

$x = 'Arcane'
$x = $x + 'Code'

First, PowerShell creates a variable in memory. For an example, we’ll say the memory is located at position 0001.

In the second line of code, PowerShell creates a second variable in memory, let’s say it is position 0002. Into position 0002, it copies the data from position 0001 then adds the Code string.

Next, it changes $x to point to memory location 0002. Finally, it marks position 0001 as no longer in use. At some point in the future, the garbage collector will clean up the memory when there is some idle time. The garbage collector is a system function that removes chunks of memory that are no longer in use, freeing up memory for other code to use.

Why This Is Bad

In the example above, we only had one variable (the one at location 0001) that needed to be garbage collected. Imagine though you were looping over thousands of records of data, building a complex string that perhaps you’ll later save to a file. The amount of work the garbage collector would need to do is enormous. It would have a negative impact on system performance, and create a slow running script.

To solve this, the StringBuilder class was created. Behind the scenes it uses a linked list. Let me step through an example a step at a time.

Step 1 – Create an empty string builder object

$output = [System.Text.StringBuilder]::new()

Step 2 – Append text to the StringBuilder variable we created

To add a string value, we will use the Append method. Note when we use methods such as Append it returns data. Most of the time we don’t need to see this. By using [void] before the line, the output of the Append method is discarded.

[void]$output.Append('Arcane')

We now have an item in memory, we’ll call it position one. This holds two values, the string value and a pointer to the next item. If there is no next item, the pointer value is null.

Position Text Pointer to next item
0001 Arcane null

Step 3 – Append a second string

[void]$output.Append('Code')

The string builder now updates the linked list.

Position Text Pointer to next item
0001 Arcane 0002
0002 Code null

Step 4 – Retrieve the data

When we go to retrieve the data, the string builder will go through the chain, assemble the final data and return it. In order to copy it into a standard string variable, we’ll need to use the ToString method to convert the result from a string builder object to a standard string.

$result = $output.ToString()

Why this is a good solution

Here, PowerShell only created one variable, then kept appending to the linked list. When we are done with the variable $output the garbage collector only has to cleanup one variable, not hundreds or (potentially) thousands.

When you only have a few items, and are sure their sizes are small, then using a string builder may not provide much benefit in terms of performance. However, when you have an unknown number of items then string builder can be a friend.

In addition to Append, string builder has several more methods that are of use. Let’s look at them now.

Append

While we just looked at using Append, I want to use this section to remind you to include proper spacing when creating your strings.

$output = [System.Text.StringBuilder]::new()
[void]$output.Append( 'PowerShell is awesome!' )
[void]$output.Append( ' It makes my life much easier.' )
[void]$output.Append( ' I think I''ll go watch some of Robert''s videos on Pluralsight.' )
$output.ToString()

This results in:

PowerShell is awesome! It makes my life much easier. I think I''ll go watch some of Robert''s videos on Pluralsight.

Note that on the second and third calls to the Append method I included a space at the beginning of the line. This was needed to make the output look like a true series of sentences, with spaces after the periods.

You could have also put spaces at the end of the lines, that is up to you and your needs when building your code.

AppendLine

When appending, you sometimes want a carriage return / line feed character added to the end of the text that was appended. To handle this, we have the Appendline method.

$output = [System.Text.StringBuilder]::new()
[void]$output.Append( 'PowerShell is awesome!' )
[void]$output.AppendLine( ' It makes my life much easier.' )
[void]$output.Append( 'I think I''ll go watch some of Robert''s videos on Pluralsight.' )
$output.ToString()

In the result, you can see the line wraps after the "…much easier." line.

PowerShell is awesome! It makes my life much easier.
I think I'll go watch some of Robert's videos on Pluralsight.

This can be handy when, for example, you are building a string that will be written out as a CSV (comma separated values) file. Each row of data will be saved as an individual line.

You may also have situations where you are building a big string that you want as something more readable. Perhaps you are building a string that will be emailed as a report. In it you’d want blank lines between each paragraph.

To accomplish this, you can just use AppendLine without passing a value into it.

$output = [System.Text.StringBuilder]::new()
[void]$output.Append( 'PowerShell is awesome!' )
[void]$output.AppendLine( ' It makes my life much easier.' )
[void]$output.AppendLine()
[void]$output.Append( 'I think I''ll go watch some of Robert''s videos on Pluralsight.' )
$output.ToString()

The output from this code is:

PowerShell is awesome! It makes my life much easier.

I think I'll go watch some of Robert's videos on Pluralsight.

AppendFormat

The third version of append is AppendFormat. It allows you to append a numerical value, and specify a string format.

In the example below, the first parameter is {0:C}. Into the spot where the 0 is, the numeric value in the second parameter, $value is placed. The :C indicates a currency format should be used.

$value = 33
$output = [System.Text.StringBuilder]::new()
[void]$output.Append( 'The value is: ' )
[void]$output.AppendFormat( "{0:C}", $value )
$output.ToString()

This results in:

The value is: $33.00

The formats supported by string builder are identical to the ones that the string data type uses.

For more information on string formatting, please see my post Fun With PowerShell String Formatting

Insert

You may have a situation where you need to insert text into the text already saved in your string builder variable. To accomplish this, we can use the Insert method.

As the first parameter we pass in the position we wish to start inserting at. The second parameter holds the text to be inserted.

$output = [System.Text.StringBuilder]::new()
[void]$output.Append( 'Arcane' )
[void]$output.Append( ' writes great blog posts.' )
[void]$output.Insert(6, 'Code')
$output.ToString()

The output of the above sample is:

ArcaneCode writes great blog posts.

Remove

In addition to inserting text, we can also remove text using the Remove method. It requires two parameters, the first is the position to start removing at, the second is the number of characters to remove.

$output = [System.Text.StringBuilder]::new()
[void]$output.Append( 'ArcaneCode' )
[void]$output.Append( ' writes great blog posts.' )
[void]$output.Remove(6, 4)
$output.ToString()

In this example I’m removing the text Code from ArcaneCode.

Arcane writes great blog posts.

Replace

You may recall that the string data type has a replace method. So too does the string builder, also named Replace. In the first parameter you pass in the character to be replaced. The second is what you want to replace it with.

$output = [System.Text.StringBuilder]::new()
[void]$output.Append( 'ArcaneCode' )
[void]$output.AppendLine( ' writes great blog posts.' )
[void]$output.Append( 'I think I''ll go watch some of Robert''s videos on Pluralsight.' )
[void]$output.Replace('.', '!')
$output.ToString()

In this simple example, I’m going to replace all periods in the text with exclamation marks.

ArcaneCode writes great blog posts!
I think I'll go watch some of Robert's videos on Pluralsight!

Be aware Replace works on the entire text held in string builder, replacing every occurance found. If you want to limit the replacements, you’d have to do so prior to any appending you do.

The Replace method is most commonly used to remove special characters from your text, perhaps a result from reading in data from file that contains things like squiggly braces and brackets.

The replacement character can be an empty string, which results in simply removing the unwanted character.

Finally, you can stack multiple methods into one operation. For example, if the string builder holds the text:

{ArcaneCode}, [arcanecode.com]

You can do:

$output.Replace('{', '').Replace('}', '').Replace('[', '').Replace(']', '')

Which results in the following text:

ArcaneCode, arcanecode.com

And you aren’t limited to stacking replaces, you can mix and match methods.

$output = [System.Text.StringBuilder]::new()
[void]$output.Append( '[ArcaneCode]' ).Replace('[', '').Replace(']', '').Insert(6, ' ')
$output.ToString()

Results in:

Arcane Code

If you get carried away this can get ugly and hard to read. But it is possible so you should know about it. There are times when it can make the code more compact and a bit easier to read, such as:

[void]$output.Replace('[', '').Replace(']', '')

Adding the first string when you create a StringBuilder object

There is one last capability to look at. When you instantiate (fancy word for create) the new string builder object, you can pass in the first text value to be stored in the string builder.

Here I’m passing in the text ArcaneCode when we create the variable.

$output = [System.Text.StringBuilder]::new('ArcaneCode')
[void]$output.Append( ' writes great blog posts.' )
$output.ToString()

The output is like you’d expect.

ArcaneCode writes great blog posts.

See Also

You may find more helpful information at the links below.

Fun With PowerShell Strings

Fun With PowerShell String Formatting

If you want to go deeper on the internals of the StringBuilder class, Andrew Lock has a great series of articles at his blog.

Conclusion

The string builder class can be a great tool for optimizing your scripts that do a lot of text manipulation.

Now that you have an understanding of the string builder class, we’re free to proceed with the next post in the ArcaneBooks project.

Advertisement