How can I make the map::find operation case insensitive?

前端 未结 11 1850
野的像风
野的像风 2020-12-01 00:23

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

map > directory;
<         


        
相关标签:
11条回答
  • 2020-12-01 00:43

    The Compare element of the map template defaults to a binary comparison class "less". Look at the implementation:

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

    You can likely create your own class that derives from binary_function (the parent class to less) and do the same comparison without case sensitivity.

    0 讨论(0)
  • 2020-12-01 00:46

    It does not by default. You will have to provide a custom comparator as a third argument. 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 like std::map< std::string, std::vector<std::string>, ci_less > myMap;

    NOTE: std::lexicographical_compare has some nitty-gritty details. String comparison isn't always straightforward if you consider locales. See this thread on c.l.c++ if interested.

    UPDATE: With C++11 std::binary_function is deprecated and is unnecessary as the types are deduced 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
        }
      };
    
    0 讨论(0)
  • 2020-12-01 00:51

    You can instantiate std::map with three parameters: type of keys, type of values, and comparison function -- a strict weak ordering (essentially, a function or functor behaving like operator< in terms of transitivity and anti-reflexivity) of your liking. Just define the third parameter to do "case-insensitive less-than" (e.g. by a < on the lowercased strings it's comparing) and you'll have the "case-insensitive map" you desire!

    0 讨论(0)
  • 2020-12-01 00:52

    In case you don't want to touch the map type (to keep it's original simplicity and efficiency), but don't mind using a slower case-insensitive find 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 Pate's idea, but not sure, since some details were a bit off (std::search?, direct string comparator?)

    0 讨论(0)
  • 2020-12-01 00:52

    For C++11 and beyond:

    #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 讨论(0)
提交回复
热议问题