String Searching - Finding and Matching Text in C#
Vaibhav • September 10, 2025
Searching within strings is a core programming skill-whether you’re looking for a keyword, checking if a
filename ends with .txt, or extracting a value from user input. C# provides a
rich set of methods for searching, matching, and extracting text: Contains,
IndexOf, StartsWith, EndsWith, and more. This article covers the most practical patterns for string
searching, including case and culture options, substring extraction, and common pitfalls.
This article builds on string comparison and formatting. We’ll focus on safe, robust searching patterns-no regular expressions yet (that’s later in the chapter).
1) Contains - does a substring exist?
Use Contains to check if a string contains another string. In modern C#, always
use the overload that takes a StringComparison for explicit case/culture
control.
string text = "C# String Searching";
bool found = text.Contains("string", StringComparison.OrdinalIgnoreCase); // True
bool notFound = text.Contains("python", StringComparison.OrdinalIgnoreCase); // False
- Default
Containsis case-sensitive and culture-sensitive (not recommended for most cases). - Always specify
StringComparisonfor clarity and safety.
2) IndexOf - where does a substring appear?
IndexOf returns the zero-based index of the first occurrence of a substring, or
-1 if not found. It’s the workhorse for locating, extracting, and splitting
text.
string sentence = "Find the position of 'the' in this sentence.";
int pos = sentence.IndexOf("the", StringComparison.OrdinalIgnoreCase); // 5
if (pos >= 0)
Console.WriteLine($"'the' found at index {pos}");
- Returns
-1if not found-always check before using the result. - Supports
StringComparisonfor case/culture control. - Can search from a specific start index:
IndexOf(value, startIndex, ...)
3) StartsWith and EndsWith - prefix and suffix
checks
Use these methods to check if a string begins or ends with a specific substring. Always use the overload with
StringComparison.
string filename = "report.PDF";
bool isPdf = filename.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase); // True
string command = "exit";
bool isExit = command.StartsWith("ex", StringComparison.OrdinalIgnoreCase); // True
- Essential for file extensions, protocol prefixes, and command parsing.
- Case/culture options make your code robust across platforms and user input.
4) Extracting substrings - Substring and IndexOf
Combine IndexOf and Substring to extract parts
of a string.
string email = "[email protected]";
int at = email.IndexOf("@");
if (at >= 0)
{
string user = email.Substring(0, at); // "user"
string domain = email.Substring(at + 1); // "example.com"
Console.WriteLine($"User: {user}, Domain: {domain}");
}
- Always check for
-1before callingSubstringto avoid exceptions. - Use
Substring(start, length)for more control.
5) Finding all occurrences - looping with IndexOf
To find every occurrence of a substring, loop with IndexOf and update the start
index.
string text = "one fish two fish red fish blue fish";
string word = "fish";
int count = 0, pos = 0;
while ((pos = text.IndexOf(word, pos, StringComparison.OrdinalIgnoreCase)) != -1)
{
count++;
pos += word.Length;
}
Console.WriteLine($"'fish' appears {count} times.");
- Advance the index by the length of the substring to avoid infinite loops.
- Works for overlapping and non-overlapping matches.
6) Case and culture: always specify your intent
All searching methods have overloads for StringComparison. Use OrdinalIgnoreCase for protocol/data, CurrentCultureIgnoreCase for user-facing text.
string input = "Straße";
bool found = input.Contains("strasse", StringComparison.CurrentCultureIgnoreCase); // True in German culture
- Culture-aware searching can treat some characters as equivalent (e.g., ß and ss in German).
- Be explicit for predictable, portable code.
7) Defensive searching - nulls, empty strings, and boundaries
- Always check for
nullor empty strings before searching. - Handle
-1results fromIndexOfand friends. - Don’t assume a substring exists-always check before extracting.
string s = null;
if (!string.IsNullOrEmpty(s) && s.Contains("test", StringComparison.OrdinalIgnoreCase))
Console.WriteLine("Found!");
8) Practical mini-project: parsing a simple key-value pair
string line = "user=vaibhav;score=100;level=5";
string[] pairs = line.Split(';');
foreach (string pair in pairs)
{
int eq = pair.IndexOf('=');
if (eq > 0)
{
string key = pair.Substring(0, eq);
string value = pair.Substring(eq + 1);
Console.WriteLine($"{key}: {value}");
}
}
- Splits the string into key-value pairs, then extracts and prints each one.
- Robust to missing or extra spaces, as long as the format is consistent.
9) Checklist - searching habits for robust code
- Always use
StringComparisonoverloads for searching methods. - Check for
-1before extracting substrings. - Use
string.Joinandstring.Splitfor lists and CSV-style data. - Loop with
IndexOfto find all matches. - Handle
nulland empty strings defensively. - Document your case/culture assumptions for future maintainers.
Summary
String searching is about more than just “does it contain?”-it’s about finding, matching, and extracting text
safely and predictably. Use Contains, IndexOf,
StartsWith, and EndsWith with explicit StringComparison options. Always check for -1
before extracting, and handle null or empty strings gracefully. With these
patterns, your code will be robust, readable, and ready for real-world text processing.