- Strings
- How to Use Substring
SubString

What is Substring?
Substring is a useful string function for copying a chunk of an existing string. It returns a sub section of the original string and depends on the selected index range. This is useful in string parsing with a repeatable pattern or fixed range. Substring is a great built in function that can be the basis of doing a lot more.
How to Use Substring?
First Function Overload
public string Substring(int startIndex)
The first one shown below with a single input gives the start index. It starts at 0. This is the simplest version. It copies from the given input start index and automatically extends the range to the end of the string which is length-1. This is useful if you know the beginning places where you want to copy and just get the rest of the string.
Second Function Overload
public string Substring(int startIndex, int length)
The next function overload has two parameters. The start index where the starting position is for the sub string and the length of the new substring. The substring length can not be longer than the remaining characters in the string. This is useful in dynamic settings where you may be searching for certain characters to deterine the length to create a substring.
Example 1
String Index for User Name
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
W | i | l | l | i | a | m | D | o | e |
For example, if we have a the user name William Doe and for this string starts at 0 and ends at index 16. If We only wanted the last name then we give a start index of 7 from the below table. Start index will be the start of the new string that is created. Then we can get only the last name.
The following code will only get the last name Doe.
string userName = "William Doe";
int startIndex = 8;
string subUserName = userName.Substring(startIndex);
Console.WriteLine($"subUserName:{subUserName}");
Output
subUserName:Doe
This function does not modify the original string. String is a immutable. Meaning that it can be changed once it is created. See the following code example.
string userName = "William Doe";
int startIndex = 8;
string subUserName = userName.Substring(startIndex);
Console.WriteLine($"userName:{userName}");
Console.WriteLine($"subUserName:{subUserName}");
Output
userName:William Doe
subUserName:Doe
Substring did not modify the variable userName as even after the function call it remains unchanged. Always keep in mind that any of the string function will return a string and not modify the original.
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
W | i | l | l | i | a | m | D | o | e | |
J | o | h | n | D | o | e |
Suppose that you had a list of names each where the space before the last name was at different positions then we can assume anymore than start index will be the same. We will find the space before the last name by using IndexOf. We could write our own function to find it but it better to find a csharp method that can do job first than create our own. See below.
List<string> userNames = new List<string>();
userNames.Add("William Doe");
userNames.Add("John Doe");
foreach (string userName in userNames)
{
int startIndex = userName.IndexOf(' ');
startIndex = startIndex + 1;
string subUserName = userName.Substring(startIndex);
Console.WriteLine($"subUserName:{subUserName}");
}
Output
subUserName:Doe
subUserName:Doe
With this code we can now find any last name that is in this format that has a space before the last name. As you may have noticed that the string function operations are useful together.
Why use Substring?
Like many things in programming, there are multiple ways of doing the same thing. Let's explore some other ways we might go about getting the last name and why substring might be better for those reason. For example, using the previous problem statement and we want to parse the user name and return only the last name. How might we go about doing that.
Character Loop
The string object can be loop through character by character. As we loop through we wait until we hit the space then save all the remaining characters to a string. Since in this case we know the user names are not long saving it in a string is fine. But if the user names were really long then it'd better to save the contents in a StrinBuilder object.
Output
userName:William Doe
subUserName:Doe
As you can see looping through each character is more code to write and generally we would like to reduce code and improve readablity.
IndexOf Function
We can find the position of the space before the last name by using the IndexOf function and then add one to find the begining of the new substring. See below.
string userName = "William Doe";
//Find where the space right before the last name
int startIndex = userName.IndexOf(' ');
//Add to the start because want the start after the space
startIndex = startIndex + 1;
string subUserName = userName.Substring(startIndex);
Console.WriteLine($"subUserName:{subUserName}");
Output
subUserName:Doe
This keeps the code simpler and more readablity. C# in general has a lot of handy functions for handling basic task for us to build upon and the string function set is an example of that.
What is Substring Best For?
I most often use substring when there are fixed index and length to copy characters from. It could be extracting a datte from a file name or some other meta data from a string such an id number or variable. Take the below example of a file name where it has meta data seperated by a underscore.
If given a file name with servername9349_5456453_02172022 reprensented by servername_processid_date. Where server name could a range of characters between 1-10 characters, processid must be between 7 characters long and the date must be 8 characters long. This is good use case to use Substring to copy the meta data out. Inspect code below.
For writing any code I would think about the logic. I want to split the string by the underscore to get an array of string where array at index 0 will be the server name. The array at index 1 will be the processid and then the array at index 2 will hold the date. Once I have the date in string then I seperate the month, day and year using substring. See code sample below.
//Starting file name value
string fileName = "servername9349_5456453_02172022";
//Break file name into 3 parts
string[] fileMetaData = fileName.Split('_');
//Assign each of the new parts to new strings for readabliity
string serverName = fileMetaData[0];
string processId = fileMetaData[1];
string dateRaw = fileMetaData[2];
//Save substring values to a new string
string monthRaw = dateRaw.Substring(0, 2);
string dayRaw = dateRaw.Substring(2, 2);
string yearRaw = dateRaw.Substring(4, 4);
//Convert String values into int types
int month = ConvertToInt(monthRaw);
int day = ConvertToInt(dayRaw);
int year = ConvertToInt(yearRaw);
//Convert int values to a DateTime object
DateTime dateTime = new DateTime(year, month, day);
//Print out remaining values
Console.WriteLine($"serverName:{serverName}");
Console.WriteLine($"processId:{processId}");
Console.WriteLine($"dateTime:{dateTime.ToLongDateString()}");
//Takes in raw value and tries to convert into a int else returns -1
int ConvertToInt(string rawValue)
{
int value = 0;
if(int.TryParse(rawValue, out value))
{
return value;
}
return -1;
}
Output
serverName: servername9349
processId:5456453
dateTime: Thursday, February 17, 2022
Substring Error Handling and Protection
The function can throw an exception in the following cases. If startIndex is less than 0 or if startIndex is greater than the string length - 1 then there is a out of bounds exeception thrown. To add protection, before using the start index is best to check wheither or not is less than 0 or greater than the size of the string.
string SubStringWrapper(string userName, int startIndex)
{
if (startIndex < 0 || startIndex > userName.Length)
{
return "";
}
string subUserName = userName.Substring(startIndex);
return subUserName;
}
//Proper use of substring so the correct last name is returned
string userName = "William Doe";
int startIndex = 8;
string subUserName = SubStringWrapper(userName, startIndex);
Console.WriteLine($"subUserName:{subUserName}");
//Invalid start index less than 0 so it returns empty string
userName = "William Doe";
startIndex = -39;
subUserName = SubStringWrapper(userName, startIndex);
Console.WriteLine($"subUserName2:{subUserName}");
//Invalid start index that is greater than total string length so returns empty string
userName = "William Doe";
startIndex = 20;
subUserName = SubStringWrapper(userName, startIndex);
Console.WriteLine($"subUserName3:{subUserName}");
Output
subUserName:Doe
subUserName2:
subUserName3:
Next take look at the second function overload with protection. We have to protect against the upper bound of the string index.
string SubStringWrapper(string userName, int startIndex, int length)
{
//We add basic bound protection on the startIndex less than 0 and great
if (startIndex < 0 || startIndex > userName.Length)
{
return "";
}
//Check to see if end bounds is greater than the string length itself
if (startIndex + length > userName.Length)
{
return "";
}
string subUserName = userName.Substring(startIndex, length);
return subUserName;
}
//Correct ussage and will return the correct last name
string userName = "William Doe";
int startIndex = 8;
//Total length of the new sub string including the start index
int length = 3;
string subUserName = SubStringWrapper(userName, startIndex, length);
Console.WriteLine($"subUserName1:{subUserName}");
//Startindex is less than 0 so it returns empty string
startIndex = -1;
length = 2;
subUserName = SubStringWrapper(userName, startIndex, length);
Console.WriteLine($"subUserName2:{subUserName}");
startIndex = 8;
//This will put the bounds over the upper limit of the index of the string so it returns empty string
length = 5;
subUserName = SubStringWrapper(userName, startIndex, length);
Console.WriteLine($"subUserName2:{subUserName}");
Output
subUserName1:Doe
subUserName2:
subUserName2:
Additional Substring Examples
Analysis Of Getting First N Characters Methods
Best First Character Removal Function In A String
Get A Substring Before Or After A Certain Character
Get A Substring Between Two Strings
How Best To Get Last N Characters
How Does Substring Work?
Substring works by looping through each character from the start index given. Then it loops through the rest of characters or has to stop after a certain number of characters. Inspect code snippet below.
using System.Text;
string SubstringCustom(string userName, int startIndex)
{
if (userName.Length < 20)
{
string subUserName = "";
for (int i = startIndex; i < userName.Length; i++)
{
subUserName += userName[i];
}
return subUserName;
}
else
{
StringBuilder sb = new StringBuilder();
for (int i = startIndex; i < userName.Length; i++)
{
sb.Append(userName[i]);
}
return sb.ToString();
}
}
string userName = "T Rex";
int startIndex = userName.IndexOf(' ');
startIndex = startIndex + 1;
string subUserName = SubstringCustom(userName, startIndex);
Console.WriteLine($"subUserName:{subUserName}");
Output
subUserName:Rex
Notice that in cases where the length of the string is less than 20 then it just adds to an another string but if the over 20 then a stringbuilder object is used for performance reasons. Which is discussed more here..
Next we'll look at the on how the second function works. With an additional parameter of length it will be a similar process as the first function except that we will need to stop the substring at startIndex + length. Inspect code below.
using System.Text;
string SubstringCustom(string userName, int startIndex, int length)
{
if (userName.Length < 20)
{
string subUserName = "";
for (int i = startIndex; i < startIndex + length; i++)
{
subUserName += userName[i];
}
return subUserName;
}
else
{
StringBuilder sb = new StringBuilder();
for (int i = startIndex; i < startIndex + length; i++)
{
sb.Append(userName[i]);
}
return sb.ToString();
}
}
string userName = "Tree Hugger";
int startIndex = userName.IndexOf(' ');
startIndex = startIndex + 1;
int length = userName.Length - startIndex;
string subUserName = SubstringCustom(userName, startIndex, length);
Console.WriteLine($"subUserName:{subUserName}");
Output
subUserName:Hugger