Finding text inside a string
Often you need to know whether a string contains some text and where it sits. In C#, the IndexOf method answers both questions. It returns the index of the first character of the first match, or -1 when the text is not there.
string sentence = "Hello World";
Console.WriteLine(sentence.IndexOf("World")); // 6
Console.WriteLine(sentence.IndexOf("xyz")); // -1
Counting from 0, "Hello World" has W at index 6, which is where "World" begins. The value -1 can never be a real index, so it works as a "not found" signal.
The variations
| Call | What it does |
|---|---|
s.IndexOf(sub) | Index of the first occurrence, or -1 |
s.LastIndexOf(sub) | Index of the last occurrence, or -1 |
s.IndexOf(sub, fromIdx) | First occurrence at or after index fromIdx |
The search text can be a string or a single char. Here they are on "Hello World", which has the letter o at indexes 4 and 7:
string sentence = "Hello World";
Console.WriteLine(sentence.IndexOf("o")); // 4
Console.WriteLine(sentence.LastIndexOf("o")); // 7
Console.WriteLine(sentence.IndexOf("o", 5)); // 7
Console.WriteLine(sentence.IndexOf('W')); // 6
Starting the search at index 5 skips the first o, so the third call finds the second one. To find every occurrence, repeat the search from one position after the last match until the result is -1.
Finding every occurrence
A single call only reports one match. To visit all of them, search again from just after the previous match, and stop when the result is -1:
string text = "banana";
int pos = text.IndexOf("a");
while (pos != -1) {
Console.WriteLine("found at " + pos);
pos = text.IndexOf("a", pos + 1);
}
This prints found at 1, found at 3 and found at 5. Starting the next search at pos + 1 is what stops the loop from finding the same match again forever.
Cutting around a match
The index is most useful when combined with Substring. Splitting an address at the @ sign looks like this:
string email = "user@example.com";
int at = email.IndexOf('@');
if (at != -1) {
Console.WriteLine(email.Substring(0, at)); // user
Console.WriteLine(email.Substring(at + 1)); // example.com
}
The if guards against the -1 case, where Substring would throw.
Case matters
By default the search is case-sensitive, so "Hello World".IndexOf("world") returns -1. To ignore case, pass a comparison option:
string sentence = "Hello World";
Console.WriteLine(sentence.IndexOf("world")); // -1
Console.WriteLine(sentence.IndexOf("world", StringComparison.OrdinalIgnoreCase)); // 6
Culture and exact comparison
When you pass a string to IndexOf, the comparison follows the current culture's rules for text, which can treat some character combinations specially. A call with a single char compares exactly. When you want a plain, exact, character-by-character search with a string, ask for it:
int i = "Hello World".IndexOf("World", StringComparison.Ordinal); // 6
For ordinary ASCII text like the lesson's, the result is the same either way. The option matters mostly when text comes from users around the world.
Two edge cases are worth remembering. Searching for an empty string returns 0, since the empty text matches at the very start. Searching for text longer than the string returns -1.
Index or yes-or-no?
If you only need to know whether the text is present, Contains reads better than comparing IndexOf with -1:
Console.WriteLine("Hello World".Contains("World")); // True
Use IndexOf when the position itself matters, for example to cut the string around the match with Substring.
The exercise, step by step
The lesson gives you sentence set to "Hello World". You add one variable below it.
- Declare an
intnamedidx. The result ofIndexOfis a whole number, sointis the right type. - Call
IndexOf("World")onsentenceand assign the result to it. The match starts at index 6.
string sentence = "Hello World";
int idx = sentence.IndexOf("World");
After this runs, idx holds 6.
Common mistakes
- Not checking for -1. Using the result as an index when nothing was found, for example in
Substring, throws an exception. Test for -1 first when the text might be missing. - Expecting a boolean.
IndexOfreturns a number. Index 0 is a real match at the start, so a test like "is the result truthy" is wrong; compare with -1 explicitly. - Wrong capitalisation. The method is
IndexOf, with a capital I and a capital O. Names such asindexOforfinddo not exist on a C# string. - A
fromIdxpast the end. Starting beyond the string's length throws anArgumentOutOfRangeException.
Key takeaways
IndexOfreturns the index of the first match, or -1 if there is none.LastIndexOffinds the last match, and a second argument sets where the search starts.- The search is case-sensitive unless you pass
StringComparison.OrdinalIgnoreCase. - Use
Containswhen only presence matters.