Recently I’ve been asked how you can make method find of std::string case insensitive. An obvious solution is to transform the strings to lower case, for instance, and perform the search in lower case. The solution has some inconvenients and a clean and nice solution is defining custom traits that ignore the case. The solution I’m showing bellow is based onĀ an example from the book “The C++ Standard Library – A Tutorial and Reference” by Nicolai M. Josuttis (Addison Wesley, 1999). The original example is available online on the author’s web page: http://www.josuttis.com/libbook/string/icstring1.cpp.html.
What I’m showing below is a modified implementation that allows working both with strings of char and wchar_t.
For the start, a custom char_traits implementation must be provided:
// define a custom traits class that ignores case templatestruct ignorecase_traits : public std::char_traits { typedef T char_type; typedef int int_type; typedef std::streamoff off_type; typedef std::streampos pos_type; typedef std::mbstate_t state_type; // Performs the assignment dst = src static void assign(char_type& dst, const char_type src) { dst = src; } // Assigns the value c to each pointer in the range [dst, dst+n), and returns dst static char_type* assign(char_type* dst, size_t n, char_type c) { return static_cast (std::memset(dst, n, c)); } // return whether c1 and c2 are equal static bool eq(const char_type& c1, const char_type& c2) { return std::toupper(c1)==std::toupper(c2); } // return whether c1 is less than c2 static bool lt(const char_type& c1, const char_type& c2) { return std::toupper(c1) < std::toupper(c2); } // compare up to n characters of s1 and s2 static int compare(const char_type* s1, const char_type* s2, std::size_t n) { for (std::size_t i=0; i < n; ++i) { if (!eq(s1[i], s2[i])) { return lt(s1[i], s2[i])?-1:1; } } return 0; } // search c in s static const char_type* find(const char_type* s, std::size_t n, const char_type& c) { for (std::size_t i=0; i < n; ++i) { if (eq(s[i], c)) { return &(s[i]); } } return 0; } };
Structure ignorecase_traits is parameterized, so that we will be able to instantiate it both for char and wchar_t. Two typedefs of basic_string with the new ignorechar_traits are necessary:
// define a special type for a string of char typedef std::basic_string> icstring; // define a special type for a strings of wchar_t typedef std::basic_string > icwstring;
To be able to print objects of type icstring or icwstring an overload of operator<< is required:
templateinline std::basic_ostream & operator << (std::basic_ostream & strm, const std::basic_string >& s) { return strm << s.c_str(); }
Having all that implemented, we can use the two types, like the shown below:
int _tmain(int argc, _TCHAR* argv[])
{
icstring is1("marius");
icstring is2("marIUS");
std::cout << std::boolalpha;
std::cout << is1 << " == " << is2 << " : " << (is1==is2) << std::endl;
icwstring iws1(L"bancila");
icwstring iws2(L"banCILA");
std::wcout << std::boolalpha;
std::wcout << iws1 << L" == " << iws2 << L" : " << (iws1==iws2) << std::endl;
if(is2.find("Riu") != std::string::npos)
std::cout << "Riu found in " << is2 << std::endl;
std::wstring ws1(L"cil");
icwstring iws3(ws1.begin(), ws1.end());
if(iws2.find(iws3) != std::string::npos)
std::wcout << iws3 << L" found in " << iws2 << std::endl;
return 0;
}
If you run the code you'll get the following output:
marius == marIUS : true bancila == banCILA : true Riu found in marIUS cil found in banCILA









