Introduction to finding strings in C++
Finding a string inside another string is a common task in C++ programming, whether you are parsing text, validating input, or building search features. The standard library provides several reliable ways to locate substrings, compare C-style strings, and iterate over collections of strings. This article explains the most widely used methods, shows concrete examples, and highlights edge cases and performance considerations. You will learn when to use std::string::find, C library functions like strstr, loops with std::getline, and regular expressions with std::regex_search.
Standard method: std::string::find
The member function std::string::find is the most idiomatic way to search for a substring in a std::string. It returns the position of the first character of the first match, or std::string::npos if no match is found. The function accepts the substring to search for, and optionally a starting position, which makes it easy to continue searching from a specific index.
Basic usage of find
To locate a substring, call find on the target string with the substring as the argument. If the return value is not equal to std::string::npos, a match exists, and the return value is the zero-based index of the first character of the match. This method is straightforward and expressive for common substring searches.
std::string text = "C++ is powerful and fun";
std::size_t pos = text.find("powerful");
if (pos != std::string::npos) {
// found at index 6
}Search with start position
You can provide a start index to continue searching after a given position. This is useful when you need to find multiple occurrences. By adjusting the start position, you can iterate through all matches without rescanning earlier parts of the string.
std::string csv = "apple,banana,apple,cherry";
std::size_t start = 0;
while (true) {
std::size_t pos = csv.find("apple", start);
if (pos == std::string::npos) break;
start = pos + 1;
// process match at pos
}| Attribute | Verified Detail | Source Type |
|---|---|---|
| Return value on success | Zero-based index of first character of substring | ISO C++ standard (cppreference) |
| Return value on failure | std::string::npos, typically -1 |
ISO C++ standard |
| Time complexity | Worst-case O(n*m) for naive implementation, often optimized | Typical library implementation notes |
| Locale behavior | Search is binary; no locale-aware case folding | Standard specification |
Searching C-style strings with strstr
When working with C-style character arrays, you can use strstr from the C standard library. strstr returns a pointer to the first occurrence of the substring, or nullptr if the substring is not found. This function operates on null-terminated character arrays and does not work directly with std::string without converting to .c_str().
const char* text = "Hello, world!";
const char* sub = "world";
const char* result = std::strstr(text, sub);
if (result != nullptr) {
// found
}Important notes about strstr
- Requires null-terminated strings; behavior is undefined if the buffer is not properly terminated.
- Works on raw bytes; it does not understand Unicode or multi-byte encodings beyond single-byte character sets.
- Cannot be used directly with
std::string; you must call.c_str(), which returns a pointer to an internal null-terminated buffer.
Finding multiple lines or tokens containing a string
If your input consists of multiple lines and you want to find lines that contain a specific substring, combining std::getline with std::string::find is a reliable approach. This pattern is common when processing text files or streams.
std::istringstream input("error: file not found\nwarning: low memory\nerror: timeout");
std::string line;
while (std::getline(input, line)) {
if (line.find("error") != std::string::npos) {
// process error line
}
}Using regular expressions with std::regex_search
For more flexible pattern matching, such as case-insensitive search or partial matches, you can use std::regex_search from the <regex> header. Regular expressions allow you to define complex search criteria without manually writing loop logic.
std::string text = "Contact: test_123@example.com";
std::regex pattern(R"(\b[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}\b)");
if (std::regex_search(text, pattern)) {
// found an email-like pattern
}Regex considerations
Regular expressions are more expressive but also more expensive than simple substring searches. Use them when you need flexibility, and prefer std::string::find for straightforward literal substring detection. Regex performance depends on pattern complexity and input size.
Case-sensitive versus case-insensitive search
By default, std::string::find and strstr are case-sensitive. If you need case-insensitive matching, you must normalize the strings yourself or use platform-specific or third-party utilities. Converting both strings to lowercase before comparison is a common approach, but you must preserve the original indices if you need to report positions.
std::string a = "Hello";
std::string b = "HELLO";
std::transform(a.begin(), a.end(), a.begin(), ::tolower);
std::transform(b.begin(), b.end(), b.begin(), ::tolower);
bool equal = (a == b);Performance and correctness notes
When searching for strings, prefer std::string::find for std::string data to benefit from standard library optimizations and clear ownership semantics. Use C library functions only when working with C-style interfaces or legacy code. Always check for failure indicators (npos for find, nullptr for strstr) before using the result to avoid undefined behavior.
- For single substring search:
std::string::find - For multiple literal substring searches: loop with
findand advancing start position - For pattern-based search:
std::regex_search - For C-style strings:
strstrwith null-terminated buffers