The complete guide to IndexOf, Searching, Matching, Case-Insensitive Lookups, Multiple Occurrences & More
Finding the index of a substring sounds simple – until you need:
- Case-insensitive searching
- First vs. last occurrence
- Searching from a specific position
- All occurrences
- Safe searching without exceptions
- Fast searching for large text
- Span-based searching (.NET 8/9/10)
- Complex patterns with Regex
- Unicode-safe operations
- High-performance scanning
This guide covers everything you will ever need for substring indexing in C# – with 40+ examples, best-practice patterns, performance notes, and production-ready snippets.
Letβs begin.
π‘ Quick Summary β The 3 Most Common Ways to Find a Substring
1) IndexOf β find first match
int index = text.IndexOf("hello");
2) LastIndexOf β find last match
int index = text.LastIndexOf("hello");
3) Case-insensitive
int index = text.IndexOf("hello", StringComparison.OrdinalIgnoreCase);
Anything else?
This guide covers all scenarios.
π Basic Example – Find Substring Index
string text = "Hello world!";
int index = text.IndexOf("world");
Console.WriteLine(index); // 6
Returns:
- 0-based index
- -1 if not found
π Find Substring Index β Case-Insensitive
string text = "Hello WORLD!";
int index = text.IndexOf("world", StringComparison.OrdinalIgnoreCase);
Console.WriteLine(index); // 6
When to use:
- User input
- Search bars
- Matching commands
- Data normalization
Best practice:
β‘οΈ Always specify StringComparison
It avoids unpredictable culture behavior.
π― Find the LAST Occurrence of a Substring
string text = "one two one two";
int index = text.LastIndexOf("two");
Console.WriteLine(index); // 12
Useful when:
- Parsing logs
- Looking for the last folder name in a path
- Backwards scanning
π’ Find the Nth Occurrence (2nd, 3rd, etc.)
public static int NthIndexOf(string text, string value, int n)
{
int index = -1;
for (int i = 0; i < n; i++)
{
index = text.IndexOf(value, index + 1, StringComparison.Ordinal);
if (index == -1) break;
}
return index;
}
Console.WriteLine(NthIndexOf("one two one two", "two", 2)); // 12
π Find All Occurrences of a Substring
public static List<int> AllIndexesOf(string text, string value)
{
var result = new List<int>();
int index = 0;
while ((index = text.IndexOf(value, index, StringComparison.Ordinal)) != -1)
{
result.Add(index);
index += value.Length;
}
return result;
}
// Example
var indexes = AllIndexesOf("banana", "an");
// Returns: [1, 3]
π« Safe Searching (Avoid Null Exceptions)
public static int SafeIndexOf(string? text, string? value)
{
if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(value))
return -1;
return text.IndexOf(value, StringComparison.Ordinal);
}
β‘ High-Performance Searching Using Span<char> (Fastest Method)
(For .NET 8/9/10 and C# 12/13/14)
ReadOnlySpan<char> span = "Hello world".AsSpan();
int index = span.IndexOf("world".AsSpan());
Why use this?
- Avoids string allocation
- Fast for parsing large logs
- Fast for CPU-tight loops
π§΅ Find Index Starting From a Specific Position
string text = "abc abc abc";
int index = text.IndexOf("abc", 4);
Console.WriteLine(index); // 4
Starts at character index 4.
Useful for:
- Skipping already-processed sections
- Sequential scanning
- Parsers and tokenizers
π Reverse Search from a Position
string text = "abc abc abc";
int index = text.LastIndexOf("abc", 5);
Console.WriteLine(index); // 4
π§ͺ Find Index Using Regex (Pattern Search)
var match = Regex.Match("abc123", @"\d+");
Console.WriteLine(match.Index); // 3
Use when:
- Searching for patterns
- Extracting numbers
- Validating formats
π§© Unicode-Safe Searching
Use:
StringComparison.Ordinal
or
StringComparison.OrdinalIgnoreCase
Why?
- Fastest
- Predictable
- Culture-safe
Avoid:
β CurrentCulture (slow + unpredictable)
β default IndexOf overload (uses culture implicitly)
π Substring Index Helper Class (Production-Ready)
public static class StringSearch
{
public static int First(string text, string value, bool ignoreCase = false)
{
return text.IndexOf(
value,
ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal
);
}
public static int Last(string text, string value, bool ignoreCase = false)
{
return text.LastIndexOf(
value,
ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal
);
}
public static List<int> All(string text, string value, bool ignoreCase = false)
{
var indexes = new List<int>();
int index = 0;
var comparison = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
while ((index = text.IndexOf(value, index, comparison)) != -1)
{
indexes.Add(index);
index += value.Length;
}
return indexes;
}
}
𧨠When to Use What – The Decision Guide
| Task | Best Method |
|---|---|
| First match | IndexOf |
| Last match | LastIndexOf |
| Case-insensitive | IndexOf(..., OrdinalIgnoreCase) |
| Find all | Custom loop |
| Nth match | Counter loop |
| High-performance | Span<char> |
| Pattern search | Regex |
| Safe search | Null checks |
π Real-World Examples
β Check if a string contains a substring (index β₯ 0)
bool contains = text.IndexOf("hello", StringComparison.OrdinalIgnoreCase) >= 0;
β Extract the part before a substring
int index = text.IndexOf(":");
string before = index == -1 ? text : text[..index];
β Extract the part after a substring
int index = text.IndexOf(":");
string after = index == -1 ? "" : text[(index + 1)..];
β Find next occurrence in a loop (tokenizer-style)
int pos = 0;
while ((pos = text.IndexOf(",", pos)) != -1)
{
Console.WriteLine(pos);
pos++;
}
π₯ Full Benchmark – Which Is Fastest?
Ranked fastest β slowest:
| Method | Speed |
|---|---|
Span<char>.IndexOf | π Fastest |
IndexOf(StringComparison.Ordinal) | Very fast |
IndexOf (default culture) | Slower |
| Regex | Slowest |
π§ Common Mistakes to Avoid
β Using IndexOf("abc") without StringComparison
β Can misbehave in Turkish locale (βiβ problem)
β Searching for empty strings
β Returns 0 (often not intended)
β Using regex for simple substring search
β Overkill + slow
β Using culture-sensitive comparison
β Inconsistent behavior across users
π Summary
This guide gave you everything: