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
Contains
is case-sensitive and culture-sensitive (not recommended for most cases). - Always specify
StringComparison
for 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
-1
if not found-always check before using the result. - Supports
StringComparison
for 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
-1
before callingSubstring
to 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
null
or empty strings before searching. - Handle
-1
results fromIndexOf
and 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
StringComparison
overloads for searching methods. - Check for
-1
before extracting substrings. - Use
string.Join
andstring.Split
for lists and CSV-style data. - Loop with
IndexOf
to find all matches. - Handle
null
and 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.