4.13. Doing a Case-Insensitive String Comparison

Problem

You have two strings, and you want to know if they are equal, regardless of the case of the characters. For example, “cat” is not equal to “dog,” but “Cat,” for your purposes, is equal to “cat,” “CAT,” or “caT.”

Solution

Compare the strings using the equal standard algorithm (defined in <algorithm>), and supply your own comparison function that uses the toupper function in <cctype> (or towupper in <cwctype> for wide characters) to compare the uppercase versions of characters. Example 4-21 offers a generic solution. It also demonstrates the use and flexibility of the STL; see the discussion below for a full explanation.

Example 4-21. Case-insensitive string comparison

1   #include <string>
2   #include <iostream>
3   #include <algorithm>
4   #include <cctype>
5   #include <cwctype>
6    
7   using namespace std;
8
9   inline bool caseInsCharCompareN(char a, char b) {
10     return(toupper(a) == toupper(b));
11  }
12
13  inline bool caseInsCharCompareW(wchar_t a, wchar_t b) {
14     return(towupper(a) == towupper(b));
15  }
16
17  bool caseInsCompare(const string& s1, const string& s2) {
18     return((s1.size() == s2.size()) &&
19            equal(s1.begin(), s1.end(), s2.begin(), caseInsCharCompareN));
20  }
21
22  bool caseInsCompare(const wstring& s1, const wstring& s2) {
23     return((s1.size() == s2.size()) &&
24            equal(s1.begin(), s1.end(), s2.begin(), caseInsCharCompareW));
25  }
26
27  int main() {
28     string s1 = "In the BEGINNING...";
29     string s2 = "In the beginning...";
30     wstring ws1 = L"The END";
31     wstring ws2 = L"the endd";
32
33     if (caseInsCompare(s1, s2))
34        cout << "Equal!\n";
35
36     if (caseInsCompare(ws1, ws2))
37        cout << "Equal!\n";
38  }

Discussion

The critical part of case-insensitive string comparison is the equality test of each corresponding pair of characters, so let’s discuss that first. Since I am using the equal standard algorithm in this approach but I want it to use my special comparison criterion, I have to create a standalone function to handle my special comparison.

Lines 9-15 of Example 4-21 define the functions that do the character comparison, caseInsCharCompareN and caseInsCharCompareW . These use toupper and towupper to convert each character to uppercase and then return whether they are equal.

Once I have my comparison functions complete, it’s time to use a standard algorithm to handle applying my comparison functions to arbitrary sequences of characters. The caseInsCompare functions defined in lines 17-25 do just that using equal. There are two overloads, one for each character type I care about. They both do the same thing, but each instantiates the appropriate character comparison function for its character type. For this example, I overloaded two ordinary functions, but you can achieve the same effect with templates. See the sidebar “Should I Use a Template?” for a discussion.

equal compares two sequence ranges for equality. There are two versions: one that uses operator==, and another that uses whatever binary predicate (i.e., takes two arguments and returns a bool) function object you supply. In Example 4-21, caseInsCharCompareN and W are the binary predicate functions.

But that’s not all you have to do—you need to compare the sizes, too. Consider equal’s declaration:

template<typename InputIterator1, typename InputIterator2,
         typename BinaryPredicate>
bool equal(InputIterator1 first1, InputIterator1 last1,
           InputIterator2 first2, BinaryPredicate pred);

Let n be the distance between first1 and last1, or in other words, the length of the first range. equal returns true if the first n elements of both sequences are equal. That means that if, given two sequences where the first n elements are equal, and the second sequence has more than n elements, equal will return true. Include a size check in your comparison to avoid this false positive.

You don’t need to encapsulate this logic in a function. Your code or your client’s code can just call the algorithm directly, but it’s easier to remember and cleaner to write this:

if (caseInsCompare(s1, s2)) {
// they are equal, do something

than this:

if ((s1.size() == s2.size()) &&
    std::equal(s1.begin(), s1.end(), s2.begin(), caseInsCharCompare<char>)) {
// they are equal, do something

whenever you want to do a case-insensitive string comparison.

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.