How can I make a card :: find case-insensitive operation?

Does the map::find method support case insensitive search? I have a map as follows:

 map<string, vector<string> > directory; 

and want the search below to ignore case:

 directory.find(search_string); 
+45
c ++ string dictionary case-insensitive stl map
Nov 26 '09 at 6:31
source share
12 answers

This is not the default. You will need to provide your own comparator as the third argument. The following snippet will help you ...

  /************************************************************************/ /* Comparator for case-insensitive comparison in STL assos. containers */ /************************************************************************/ struct ci_less : std::binary_function<std::string, std::string, bool> { // case-independent (ci) compare_less binary function struct nocase_compare : public std::binary_function<unsigned char,unsigned char,bool> { bool operator() (const unsigned char& c1, const unsigned char& c2) const { return tolower (c1) < tolower (c2); } }; bool operator() (const std::string & s1, const std::string & s2) const { return std::lexicographical_compare (s1.begin (), s1.end (), // source range s2.begin (), s2.end (), // dest range nocase_compare ()); // comparison } }; 

Use it as std::map< std::string, std::vector<std::string>, ci_less > myMap;

NOTE : std :: lexicographic_compare contains some minor details. Comparing strings is not always easy given the locales. See this thread on cl c ++ if interested.

UPDATE : with c ++ 11, std::binary_function deprecated and not needed, since types are inferred automatically.

  struct ci_less { // case-independent (ci) compare_less binary function struct nocase_compare { bool operator() (const unsigned char& c1, const unsigned char& c2) const { return tolower (c1) < tolower (c2); } }; bool operator() (const std::string & s1, const std::string & s2) const { return std::lexicographical_compare (s1.begin (), s1.end (), // source range s2.begin (), s2.end (), // dest range nocase_compare ()); // comparison } }; 
+64
Nov 26 '09 at 6:39
source share

Here are a few other alternatives, including one that runs much faster.

 #include <map> #include <string> #include <cstring> #include <iostream> #include <boost/algorithm/string.hpp> using std::string; using std::map; using std::cout; using std::endl; using namespace boost::algorithm; // recommended in Meyers, Effective STL when internationalization and embedded // NULLs aren't an issue. Much faster than the STL or Boost lex versions. struct ciLessLibC : public std::binary_function<string, string, bool> { bool operator()(const string &lhs, const string &rhs) const { return strcasecmp(lhs.c_str(), rhs.c_str()) < 0 ; } }; // Modification of Manuel answer struct ciLessBoost : std::binary_function<std::string, std::string, bool> { bool operator() (const std::string & s1, const std::string & s2) const { return lexicographical_compare(s1, s2, is_iless()); } }; typedef map< string, int, ciLessLibC> mapLibc_t; typedef map< string, int, ciLessBoost> mapBoost_t; int main(void) { mapBoost_t cisMap; // change to test other comparitor cisMap["foo"] = 1; cisMap["FOO"] = 2; cisMap["bar"] = 3; cisMap["BAR"] = 4; cisMap["baz"] = 5; cisMap["BAZ"] = 6; cout << "foo == " << cisMap["foo"] << endl; cout << "bar == " << cisMap["bar"] << endl; cout << "baz == " << cisMap["baz"] << endl; return 0; } 
+23
Jun 09 '10 at 20:45
source share

You can create a std::map with three parameters: the type of keys, the type of values, and the comparison function β€” a strict weak order (in essence, a function or functor behaves like an operator< in terms of transitivity and anti-reflexivity) to your taste. Just define the third parameter to make it β€œcase insensitive less” (for example, a < for lines with the bottom line that it compares) and you will get the β€œcase insensitive card” you want!

+6
Nov 26 '09 at 6:39
source share

I am using the following:

 bool str_iless(std::string const & a, std::string const & b) { return boost::algorithm::lexicographical_compare(a, b, boost::is_iless()); } std::map<std::string, std::string, boost::function<bool(std::string const &, std::string const &)> > case_insensitive_map(&str_iless); 
+5
Jan 06 '10 at 9:05
source share

If you do not want to touch the type of card (to preserve its original simplicity and efficiency), but do not mind using the slower case-insensitive search function (O (N)):

 string to_lower(string s) { transform(s.begin(), s.end(), s.begin(), (int(*)(int)) tolower ); return s; } typedef map<string, int> map_type; struct key_lcase_equal { string lcs; key_lcase_equal(const string& s) : lcs(to_lower(s)) {} bool operator()(const map_type::value_type& p) const { return to_lower(p.first) == lcs; } }; map_type::iterator find_ignore_case(map_type& m, const string& s) { return find_if(m.begin(), m.end(), key_lcase_equal(s)); } 

PS: Maybe it was Roger Pat's idea, but not sure, because some details were turned off a bit (std :: search ?, direct string comparator?)

+4
Nov 26 '09 at 17:29
source share

No, you cannot do this with find , as in this case there will be several matches. For example, when you insert, you can do something like map["A"] = 1 and map["a"] = 2 , and now if you want the case insensitive map.find("a") What is the expected return value? The easiest way to solve this problem is to insert a line into the card in only one case (in upper or lower case), and then use the same case when you perform a search.

+3
Nov 26 '09 at 6:36
source share

The default map template Compare element is used for the "less" binary comparison class. Look at the implementation:

http://www.cplusplus.com/reference/std/functional/less/

You can probably create your own class that comes from binary_function (parent class to smaller) and do the same comparison without case sensitivity.

+1
Nov 26 '09 at 6:37
source share

Tested:

 template<typename T> struct ci_less:std::binary_function<T,T,bool> { bool operator() (const T& s1,const T& s2) const { return boost::ilexicographical_compare(s1,s2); }}; ... map<string,int,ci_less<string>> x=boost::assign::map_list_of ("One",1) ("Two",2) ("Three",3); cout << x["one"] << x["TWO"] <<x["thrEE"] << endl; //Output: 123 
+1
Jun 27 '13 at 15:00
source share

Implement the std :: less function and compare by changing both parameters to the same one.

0
Nov 26 '09 at 6:49
source share

For C ++ 11 and above:

 #include <strings.h> #include <map> #include <string> namespace detail { struct CaseInsensitiveComparator { bool operator()(const std::string& a, const std::string& b) const noexcept { return ::strcasecmp(a.c_str(), b.c_str()) < 0; } }; } // namespace detail template <typename T> using CaseInsensitiveMap = std::map<std::string, T, detail::CaseInsensitiveComparator>; int main(int argc, char* argv[]) { CaseInsensitiveMap<int> m; m["one"] = 1; std::cout << m.at("ONE") << "\n"; return 0; } 
0
Mar 15 '17 at 11:31 on
source share

I would like to introduce a short solution without using Boost or templates. Starting with C ++ 11, you can also provide a lambda expression as a custom comparator for your map. For a POSIX-compatible system, the solution might look like this:

 auto comp = [](const std::string& s1, const std::string& s2) { return strcasecmp(s1.c_str(), s2.c_str()) < 0; }; std::map<std::string, std::vector<std::string>, decltype(comp)> directory(comp); 

Ideone Code

For Window, strcasecmp() does not exist, but you can use _stricmp() instead:

 auto comp = [](const std::string& s1, const std::string& s2) { return _stricmp(s1.c_str(), s2.c_str()) < 0; }; std::map<std::string, std::vector<std::string>, decltype(comp)> directory(comp); 

Note: depending on your system and whether you need to support Unicode or not, you may need to compare strings in a different way. This Q&A gives a good start.

0
Mar 15 '19 at 14:12
source share

So, back to the original question: is there a way to implement map :: find () case-insensitive on a regular std :: map case-sensitive?

As mentioned above by A. Haferburg: "in fact, the other answers are misleading. They make the whole map case insensitive, the question is asked only to make map :: find () case insensitive."

0
Apr 04 '19 at 9:15
source share



All Articles