Substring
The C# Substring
method extracts a fragment of a string
based on character positions. We specify a start and length (both ints) to describe this fragment.
With Substring
, we must be careful to pass in arguments that are within the bounds of the string
. And for performance, avoiding Substring()
is usually helpful.
We invoke Substring
to extract parts of a string
into a new string
. We use 1 or 2 arguments—the first is the start index, and the second is the desired length.
Substring
with just the starting index of the substring. Strings are indexed with the first character 0.Substring
—this is the count of characters in the substring we want.using System; string input = "abcd"; // Part 1: get substring starting at index 2. // ... This continues until the end of the string. string result = input.Substring(2); Console.WriteLine($"RESULT: {result}"); // Part 2: use a second argument. // ... Start at index 1. // ... Continue for 2 places. string result2 = input.Substring(1, 2); Console.WriteLine($"RESULT: {result2}");RESULT: cd RESULT: bc
Here we take several chars in the middle of a string
and place them into a new string
. To take a middle substring, pass 2 arguments to Substring
.
Substring
is the start index. With index 3, we begin at the start of the fourth character "T."using System; string input = "OneTwoThree"; // Take substring. string result = input.Substring(3, 3); Console.WriteLine("RESULT: {0}", result);RESULT: Two
We can get just the first part of a string
. Here we eliminate the last 2 characters from the input string. Substring()
returns a new string
without them.
string
. It will cause an error if the string
is too short—a check would be needed.Remove
method can be used to remove parts of strings—it internally calls Substring
with the correct arguments.using System; string input = "abcde"; // Take beginning part. string result = input.Substring(0, input.Length - 2); Console.WriteLine("RESULT: {0}", result);RESULT: abc
IndexOf
with Substring
The IndexOf
method is made to be used with Substring
(and other String
methods). Here we want to parse a string
by finding a separator.
IndexOf
to locate the position of the separator in the string. If found, the returned value is never -1.Substring
to get the following part. We add the separator's length to the start index.using System; string value = "Unit: 300 V"; string separator = ": "; // Part 1: get index of separator. int separatorIndex = value.IndexOf(separator); // Part 2: if separator exists, get substring. if (separatorIndex >= 0) { string result = value.Substring(separatorIndex + separator.Length); Console.WriteLine("RESULT: {0}", result); }RESULT: 300 V
Substring()
must be passed arguments within the range of the string
. Exceptions can help us fix problems faster. This example triggers the ArgumentOutOfRangeException
.
Substring
with a length past the end of the source string
. This causes an ArgumentOutOfRangeException
.using System; string input = "OneTwoThree"; // Part 1: try negative start index. try { string sub = input.Substring(-1); } catch (Exception ex) { Console.WriteLine(ex); } // Part 2: try excessive length. try { string sub = input.Substring(0, 100); } catch (Exception ex) { Console.WriteLine(ex); }System.ArgumentOutOfRangeException System.String.InternalSubStringWithChecks System.ArgumentOutOfRangeException System.String.InternalSubStringWithChecks
If a string
may be null
, we need to test it against null
before using Substring
on it. We cannot take a Substring
of a null
string
.
string.IsNullOrEmpty
to prevent a NullReferenceException
. This also checks for an empty string
.using System; string value = null; // This is safe. if (!string.IsNullOrEmpty(value)) { Console.WriteLine(value.Substring(1)); } // This will cause an exception. Console.WriteLine(value.Substring(1));Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
It is possible to take a one-character substring. But if we use the indexer to get a char
, this will be faster—a char
is a value, and does not require a heap allocation.
using System; string value = "cat"; // ... In many programs, we can use a char instead of Substring. Console.WriteLine(value[0]); Console.WriteLine(value.Substring(0, 1));c c
With logic, we can avoid invoking Substring
. Suppose a program gets the same Substring
over and over again. We can handle this case in code, and return a literal.
SubstringFirst3
that optimizes the case of getting the first 3 letters of the string
"Windows."using System; class Program { static string SubstringFirst3(string value) { // ... Use logic to avoid creating a new string. if (value == "Windows") { return "Win"; } else { return value.Substring(0, 3); } } static void Main() { Console.WriteLine(SubstringFirst3("Windows")); Console.WriteLine(SubstringFirst3("Computer")); } }Win Com
char
arrayString
-related allocations can be a burden. Here we see if taking characters and putting them into a char
array is faster than calling Substring
.
char
array and assigns elements from the source string
. Then it creates a string
with a constructor.Substring()
method—it is shorter, simpler, and faster.Substring
is faster. But if we want to extract only certain characters, consider the char
array approach shown.Substring
when it has equivalent behavior. Code is shorter, simpler and easier to read.using System; using System.Diagnostics; const int _max = 1000000; const string value = "onetwothree"; // Version 1: create new string from char array. var s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { char[] array = new char[3]; array[0] = value[3]; array[1] = value[4]; array[2] = value[5]; string result = new string(array); if (result == null) { return; } } s1.Stop(); // Version 2: use Substring. var s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { string result = value.Substring(3, 3); if (result == null) { return; } } 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.19 ns new char[], new string() 13.58 ns Substring
char
Suppose we have a string
and we want to get a single character from it. A 1-char substring is possible, but accessing the char
directly is a better option.
char
instead.using System; using System.Diagnostics; const int _max = 1000000; const string value = "jounce"; // Version 1: get 1-character substring. var s1 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { string firstLetter = value.Substring(0, 1); if (firstLetter != "j") { return; } } s1.Stop(); // Version 2: access char directly. var s2 = Stopwatch.StartNew(); for (int i = 0; i < _max; i++) { char firstLetter = value[0]; if (firstLetter != 'j') { return; } } 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"));18.07 ns Substring, 1-char 0.99 ns Access char directly
Substring()
allocates a new string
. We invoke it with 1 or 2 arguments—the start and length. Avoiding Substring
when possible is often a worthwhile optimization.