4.14. Doing a Case-Insensitive String Search

Problem

You want to find a substring in a string without regard for case.

Solution

Use the standard algorithms transform and search, defined in <algorithm>, along with your own special character comparison functions, similar to the approach presented in. Example 4-22 shows how to do this.

Example 4-22. Case-insensitive string search

#include <string>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <cctype>

using namespace std;

inline bool caseInsCharCompSingle(char a, char b) {
   return(toupper(a) == b);
}

string::const_iterator caseInsFind(string& s, const string& p) {
   string tmp;

   transform(p.begin(), p.end(),             // Make the pattern
             back_inserter(tmp),                 // upper-case
             toupper);

   return(search(s.begin(), s.end(),         // Return the iter- 
                 tmp.begin(), tmp.end(),     // ator returned by
                 caseInsCharCompSingle));        // search
}

int main() {
   string s = "row, row, row, your boat";
   string p = "YOUR";
   string::const_iterator it = caseInsFind(s, p);

   if (it != s.end()) {
      cout << "Found it!\n";
   }
}

By returning an iterator that refers to the element in the target string where the pattern string starts, you ensure ease of compatibility with other standard algorithms since most of them accept iterator arguments.

Discussion

Example 4-22 demonstrates the usual mode of operation when working with standard algorithms. Create the functions that do the work, then plug them into the most appropriate algorithms as function objects. The charInsCharCompSingle function does the real work here but, unlike Example 4-21, this character comparison function only uppercases the first argument. This is because a little later in caseInsFind, I convert the pattern string to all uppercase before using it to search to avoid having to uppercase each pattern character multiple times.

Once the comparison function is out of the way, use the transform and search standard algorithms to do two things. Use transform to uppercase the entire pattern (but not the target string). After that, use search with the comparison function to find the location of the substring.

Remember that standard algorithms operate on sequences, not just strings. They are general algorithms that operate on, primarily but not exclusively, the standard containers, but they make no assumptions about the contents of the containers. All the standard algorithms care about is that you supply a comparison function (or if not, they use the default operators) that somehow compares two elements and returns a bool indicating whether the test is true or false.

There is one thing I should point out that looks odd in Example 4-22. You can see that caseInsCompare returns a const_iterator, as in

string::const_iterator caseInsFind(const string& s,
                                   const string& p)

What if you want to modify the element that the returned iterator points to? This is a reasonable request. The reason it is const is because the strings being passed into caseInsFind are const, and therefore you can’t get a non-const iterator to a const string. If you want an iterator you can use to modify the string, remove the const from the parameters and change the function declaration to return a string::iterator instead.

Get C++ Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.