What find does
Finding out whether a piece of text appears inside a string, and where, is one of the most common string tasks. In C++ the tool is the member function find of std::string. It scans the string from the left and returns the index of the first character of the first match. Indexes start at 0, like everything else in C++.
string sentence = "Hello World";
cout << sentence.find("World"); // 6
cout << sentence.find("o"); // 4
"World" begins at index 6 because H e l l o and the space take up indexes 0 to 5. The second call finds the first o, the one in "Hello", not the one in "World".
You can search for a string literal, another string variable, or a single character.
When there is no match
If the text is absent, find cannot return a valid index, so it returns a special constant, string::npos. It is the largest value the index type can hold. The index type is size_t, an unsigned integer, so npos is a huge positive number rather than -1. Always compare against the constant by name:
if (sentence.find("xyz") == string::npos) {
cout << "not found";
}
This is the check to write every time the text might be missing. Never test find(...) > 0 or find(...) != -1 to mean "found". The first is wrong when the match is at index 0, and the second depends on a conversion between signed and unsigned numbers.
rfind and the start position
Two variations cover most of the remaining needs:
s.rfind(sub)searches from the right and returns the index of the last occurrence.s.find(sub, start)begins the search at indexstartinstead of 0, which lets you find the second occurrence, the third, and so on.
With t = "one two one", t.find("one") is 0, t.rfind("one") is 8, and t.find("one", 1) is also 8, because the search skips the match at index 0.
To visit every occurrence, repeat find with a start position just after the previous match, and stop when the result is string::npos.
The type of the result
The return type is size_t (spelled string::size_type in full), not int. For short strings it makes no practical difference, and the lesson stores the result in an int. When the text is missing, converting npos to an int gives -1 on common compilers, which is why that code appears to work. For code you want to keep correct, store the result in a size_t (or auto) and compare it with string::npos before doing anything else with it.
The same rule explains why using the result without checking is dangerous. An index of string::npos passed to another string function, such as replace or substr, makes it throw std::out_of_range.
The exercise, step by step
The editor starts with string sentence = "Hello World";. The task is to find where "World" begins.
- Call
findonsentencewith the text to look for,"World". The call is written with a dot, becausefindbelongs to the string. - Store the result in a new
intvariable namedidx, declared below the existing line.
string sentence = "Hello World";
int idx = sentence.find("World");
The value of idx is 6. The text is present in this exercise, so no npos check is needed here, but in a real program you would add one.
Common mistakes
- Forgetting that the result is an index, not a count or a boolean. A match at the very start returns 0, which is a valid result.
- Using the result without a
nposcheck. The result then feeds a huge number into whatever comes next. - Mixing up case.
findis case sensitive, sosentence.find("world")does not match "World". Lowercase both strings first if you want a case-insensitive search. - Expecting every occurrence.
findreturns only one index. Loop with the start position to get the rest.
Key takeaways
s.find(sub)returns the index of the first match,s.rfind(sub)the last, ands.find(sub, start)searches fromstart.- A missing match gives
string::npos. Compare with it by name. - Check for
nposbefore you use the index.