Replace
Before using strings, C# programs must often modify them. A part of a string
can be changed (or removed) with Replace()
. We pass in "before" and "after" arguments.
A key detail of Replace()
is that it will replace all instances of the string
it finds, not just the first. If we use an empty replacement value, it removes the matches.
Here we call the Replace
method. It is important that we assign Replace
's result to a variable. It does not modify the string
in-place.
string
variable "input" which contains the first letters of the alphabet.Replace
, changing the "abc" to the 2-letter string
"hi," and then print the result.Replace()
: the first is the part we want to change, and the second is the replacement string
.using System; // Step 1: declare and print string. string input = "abcdef"; Console.WriteLine(input); // Step 2: replace part with new string. // ... Assign to the Replace method's result. string output = input.Replace("abc", "hi"); Console.WriteLine(output);abcdef hidef
The Replace
method changes every instance of the specified substring. This can sometimes result in unexpected behavior—we must be careful.
string
.Replace()
2 times. A second Replace()
would not do anything.using System; const string value = "abcabc"; Console.WriteLine("BEFORE: " + value); // Store the result of Replace() in a variable. // ... All instances of the substring are replaced. string modified = value.Replace("bc", "?"); Console.WriteLine("AFTER: " + modified);BEFORE: abcabc AFTER: a?a?
Replace
, StringBuilder
With StringBuilder
, Replace
works the same way as with strings, but we do not need to assign the result to anything.
StringBuilder
. We use the constructor to initialize the StringBuilder
with a short string.string
"z." No string
copies occur at this step.Insert
to place a new string
part at the start of the StringBuilder
.using System; using System.Text; const string value = "abcdef"; // Step 1: create new StringBuilder from string. StringBuilder builder = new StringBuilder(value); Console.WriteLine(builder); // Step 2: replace the first part. // ... The result doesn't need assignment. builder.Replace("abc", "z"); Console.WriteLine(builder); // Step 3: insert the string at the beginning. builder.Insert(0, "y:"); Console.WriteLine(builder);abcdef zdef y:zdef
Regex.Replace
The string.Replace()
method is limited in the kinds of substrings it can match. With Regex
we can match met a-characters like "digit" or "non-word."
char
and a non-word char
(like a space).string
parsing method could be written, and the result would likely be faster than Regex.Replace
—but it would be more work.using System; using System.Text.RegularExpressions; string value = "bird0 cat1 bird2 cat2 bird."; Console.WriteLine("BEFORE: " + value); // Match all strings starting with "bird" and ending with a digit and non-word character. // ... Replace them with an empty string literal. string result = Regex.Replace(value, @"bird\d\W", ""); Console.WriteLine("AFTER: " + result);BEFORE: bird0 cat1 bird2 cat2 bird. AFTER: cat1 cat2 bird.
Most parameters (including null
) will not cause an exception in Replace()
. But if we try to replace an empty string
, an ArgumentException
is encountered.
string
—would the result (in some cases) be an infinitely long string
?class Program { static void Main() { string test = "test"; // Cannot replace an empty string. string result = test.Replace("", ""); } }Unhandled Exception: System.ArgumentException: String cannot be of zero length. Parameter name: oldValue at System.String.ReplaceInternal(String oldValue, String newValue) at System.String.Replace(String oldValue, String newValue) at Program.Main() in ...
NullReferenceException
As with other string
methods, we cannot call Replace
on a null
instance. If the source string
may be null
, we must wrap it in an if
-check.
IsNullOrEmpty
to test for empty or null
strings—an empty string
will not need replacements.class Program { static void Main() { string value = null; value.Replace("bird", "?"); } }Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object. at Program.Main() in ...
StringBuilder
Sometimes StringBuilder
can lead to performance improvements. The string
type in C# can be faster in many situations.
Replace
on a string
3 times. We change all the animal names in the text string
.StringBuilder
and then call Replace
on the StringBuilder
3 times.string
directly when calling Replace
. This was tested on .NET 5 for Linux in 2021.using System; using System.Diagnostics; using System.Text; const int _max = 1000000; var text = "bird, frog and dog"; var s1 = Stopwatch.StartNew(); // Version 1: use string Replace. for (int i = 0; i < _max; i++) { string result = text; result = result.Replace("bird", "cat"); result = result.Replace("frog", "fox"); result = result.Replace("dog", "fish"); } s1.Stop(); var s2 = Stopwatch.StartNew(); // Version 2: use StringBuilder. for (int i = 0; i < _max; i++) { var builder = new StringBuilder(text); builder.Replace("bird", "cat"); builder.Replace("frog", "fox"); builder.Replace("dog", "fish"); } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns"));142.75 ns Replace (3) 472.82 ns StringBuilder Replace (3)
Contains
Replace
can be optimized. Suppose in our program many string
replacements must be done. But often nothing is changed in actual strings.
Replace
call son the parameter to the method.Contains
method around all the Replace
calls with the specified common string
literal.Replace
calls are only run when the common pattern is found. Often the string
is searched less.Contains
to reduce the number of replacements needed—fewer searches are done.using System; using System.Diagnostics; class Program { static string ReplaceAll(string text) { text = text.Replace("<span>Cat ", "<span>Cats "); text = text.Replace("<span>Clear ", "<span>Clears "); text = text.Replace("<span>Dog ", "<span>Dogs "); text = text.Replace("<span>Draw ", "<span>Draws "); return text; } static string ReplaceAllWithContains(string text) { if (text.Contains("<span>C")) { text = text.Replace("<span>Cat ", "<span>Cats "); text = text.Replace("<span>Clear ", "<span>Clears "); } if (text.Contains("<span>D")) { text = text.Replace("<span>Dog ", "<span>Dogs "); text = text.Replace("<span>Draw ", "<span>Draws "); } return text; } const int _max = 1000000; static void Main() { Console.WriteLine(ReplaceAll("<span>Dog 100</span>")); Console.WriteLine(ReplaceAllWithContains("<span>Dog 100</span>")); var s1 = Stopwatch.StartNew(); // Version 1: use Replace. for (int i = 0; i < _max; i++) { string result = ReplaceAll("<span>Dog 100</span>"); } s1.Stop(); var s2 = Stopwatch.StartNew(); // Version 2: use Contains and Replace. for (int i = 0; i < _max; i++) { string result = ReplaceAllWithContains("<span>Dog 100</span>"); } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); } }<span>Dogs 100</span> <span>Dogs 100</span> 128.76 ns ReplaceAll 98.89 ns ReplaceAllWithContains
Will a Replace
call that makes no changes in the string return faster? This helps us design faster programs by giving us more information about how Replace
works.
Replace
, but the arguments given will cause no change to occur.string
that exists inside the input string—so a change does occur.Replace()
when the method does not make a change to the string
data. This case is optimized.using System; using System.Diagnostics; const int _max = 1000000; string test = "bird"; var s1 = Stopwatch.StartNew(); // Version 1: no change made. for (int i = 0; i < _max; i++) { string result = test.Replace("z", "a"); } s1.Stop(); var s2 = Stopwatch.StartNew(); // Version 2: changed string. for (int i = 0; i < _max; i++) { string result = test.Replace("b", "a"); } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns"));22.24 ns Replace, no change 39.09 ns Replace, changed string
Sometimes, we can use char
arguments instead of 1-char strings. We test a program that calls Replace
with different kinds of arguments.
Replace()
method with 2 char
arguments.string
arguments to modify the string
that is returned by Replace
.Replace
calls that use chars are faster. When possible, prefer smaller arguments like char
.using System; using System.Diagnostics; const int _max = 1000000; string test = "cat"; var s1 = Stopwatch.StartNew(); // Version 1: use char argument. for (int i = 0; i < _max; i++) { string result = test.Replace('c', 'z'); } s1.Stop(); var s2 = Stopwatch.StartNew(); // Version 2: use string argument. for (int i = 0; i < _max; i++) { string result = test.Replace("c", "z"); } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns"));19.85 ns Replace, char 35.60 ns Replace, string
For strings, Replace
always returns a modified copy. It replaces all instances of a match. Replace()
is fast in modern versions of .NET.