System.String has dozens of methods. This page covers the ones that do most of the work in real code, with the options and the traps that matter. One rule applies to all of them: strings are immutable, so every method returns a new string (or a value) and leaves the original unchanged. s.Trim(); on its own line does nothing; write s = s.Trim();.
Split
Split breaks a string into a string[] at each separator:
Output:
3
apple
banana
cherry
red/green/ blue/yellow
INFO/started/ready
Notice " blue": Split cuts exactly at the separator and keeps any spaces. Two options handle the common cleanups:
Output:
5
4
c#|dotnet|linq
key=[Subject] value=[Re: Meeting: Friday]
3
RemoveEmptyEntries drops parts of length zero but not parts that are only spaces (" " survived above). .NET 5 added StringSplitOptions.TrimEntries, and the two can be combined: tags.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) gives the clean result in one call. Passing null as the separator splits on any whitespace, which is the usual way to split text into words.
Splitting on a single char (Split(',')) needs no array. The overloads that take a char plus options, or a single string, exist from .NET Core 2.0 on; the array forms used above work everywhere, including .NET Framework and Unity.
Replace
Replace substitutes every occurrence of a character or substring:
Output:
5551234567
555.123.4567
Cats and dogs and CATS
dogs and dogs and dogs
dogs and cats and CATS
too many spaces
The overload with a StringComparison argument is available on .NET Core 2.0 and later. On .NET Framework, case-insensitive replacement is done with Regex.Replace(text, Regex.Escape(find), replacement, RegexOptions.IgnoreCase). Replacing with "" is how you remove characters, and chaining several Replace calls is fine for a handful of substitutions.
Contains, IndexOf, StartsWith and EndsWith
These methods search. Contains answers yes or no; IndexOf and LastIndexOf return a position, or -1 when there is no match; StartsWith and EndsWith test the ends.
Output:
True
False
True
True
8
30
28
-1
16
False
True
True
All of these are case-sensitive by default. Contains(string, StringComparison) exists on .NET Core 2.1 and later; the IndexOf(...) >= 0 form works on every version, which is why you see it in older code. IndexOfAny(new[] { ',', ';' }) finds the first of several characters.
A culture note: without a StringComparison, StartsWith, EndsWith, IndexOf(string) and CompareTo use culture-sensitive rules, while Contains, Replace and IndexOf(char) compare ordinally. Passing StringComparison.Ordinal or OrdinalIgnoreCase explicitly removes the ambiguity, and code analyzers recommend it.
Trim, TrimStart and TrimEnd
Trim removes whitespace (spaces, tabs, newlines) from both ends. TrimStart and TrimEnd remove from one end. Given characters, they remove those instead:
Output:
[ana@example.com]
[ana@example.com \n]
[ x]
SALE
7
path/to/dir
1,234.50
Trim only touches the ends; spaces inside the string are kept. TrimStart('0') on "000" returns an empty string, so parse number strings with int.Parse rather than stripping zeros by hand.
ToUpper and ToLower
Output:
MAYA LOPEZ
maya lopez
MAYA LOPEZ
Coffee
ToUpper and ToLower follow the current culture, so in Turkish "i".ToUpper() is a dotted capital İ, not I. For identifiers, keys and file names use ToUpperInvariant and ToLowerInvariant. For comparing, use a StringComparison instead of converting case at all (see strings). To capitalize every word, CultureInfo.CurrentCulture.TextInfo.ToTitleCase(text) exists, though it leaves words that are already all uppercase alone.
PadLeft and PadRight
Padding adds characters until the string reaches a total width. It is handy for fixed-width output and for zero-padding codes:
Output:
000042
[Tea ]
[ Tea]
Too long already
Espresso.. 2.40
Latte..... 3.60
Mocha..... 3.95
Interpolation alignment ({name,-10}) does the same job inline.
Join and Concat
string.Join puts a separator between the elements of any collection. string.Concat joins with no separator:
Output:
Ana, Ben, Chloe
1 + 2 + 3
True
2026-09
AnaBenChloe
Ana, Ben and Chloe
Join is the right way to build a delimited list: there is no trailing separator to remove, and it builds the result in one pass. It calls ToString() on each element, so it works with numbers and objects as well.
Reverse, Insert and Remove
There is no string.Reverse method that returns a string. The two usual approaches:
Output:
desserts
desserts
True
2026/Q3-09
Hello
HelloWorld
word.Reverse() alone returns an IEnumerable<char>, not a string; printing it shows a type name. Both methods reverse UTF-16 code units, which breaks characters made of two units (most emoji) and letters with combining accents.
Quick reference
| Task | Method |
|---|---|
| Split into parts | s.Split(','), s.Split(new[] {',', ';'}, StringSplitOptions.RemoveEmptyEntries) |
| Replace all | s.Replace("a", "b") |
| Contains | s.Contains("x"), s.Contains("x", StringComparison.OrdinalIgnoreCase) |
| Position | s.IndexOf("x"), s.LastIndexOf('x') (-1 if absent) |
| Starts or ends with | s.StartsWith("x"), s.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase) |
| Remove whitespace at ends | s.Trim(), s.TrimStart(), s.TrimEnd() |
| Change case | s.ToUpper(), s.ToLowerInvariant() |
| Pad to a width | s.PadLeft(6, '0'), s.PadRight(10) |
| Join a collection | string.Join(", ", items) |
| Part of a string | s.Substring(start, length) |
| Insert or delete | s.Insert(i, "x"), s.Remove(i, count) |
| Null or blank check | string.IsNullOrWhiteSpace(s) |
Frequently Asked Questions
How do I split a string in C#?
text.Split(',') returns a string[] of the parts between commas. Pass several separators as an array (Split(new[] { ',', ';' })), a string separator with Split(new[] { ", " }, StringSplitOptions.None), and StringSplitOptions.RemoveEmptyEntries to drop empty parts produced by doubled separators. .NET 5 added StringSplitOptions.TrimEntries to trim each part.
How do I check if a string contains another string, ignoring case?
On .NET Core 2.1 and later: text.Contains("error", StringComparison.OrdinalIgnoreCase). On .NET Framework, which lacks that overload, use text.IndexOf("error", StringComparison.OrdinalIgnoreCase) >= 0. Avoid text.ToLower().Contains(...), which allocates a copy and can misbehave in some cultures.
Does String.Replace change the original string in C#?
No. Strings are immutable, so Replace returns a new string with every occurrence replaced and leaves the original as it was. Write s = s.Replace("old", "new");. It replaces all occurrences, not just the first; to replace only the first, find it with IndexOf and rebuild the string, or call Replace on a Regex instance with a count of 1.
What does IndexOf return if the string is not found?
-1. Any result of 0 or greater is the zero-based position of the first match. Always check for -1 before using the result as an index, because Substring(-1) throws and Substring(-1 + 1) silently returns the whole string. LastIndexOf works the same way, searching from the end.
How do I reverse a string in C#?
Copy it into a char array, reverse the array, and build a new string: char[] a = s.ToCharArray(); Array.Reverse(a); string r = new string(a);. With LINQ, new string(s.Reverse().ToArray()) does the same. Both reverse UTF-16 units, so emoji and combining accents can come out scrambled.