【问题标题】:How can I make the map::find operation case insensitive?如何使 map::find 操作不区分大小写?
【发布时间】:2010-12-20 14:07:37
【问题描述】:

map::find 方法是否支持不区分大小写的搜索?我有一张地图如下:

map<string, vector<string> > directory;

并希望以下搜索忽略大小写:

directory.find(search_string);

【问题讨论】:

    标签: c++ string dictionary stl case-insensitive


    【解决方案1】:

    不,您不能使用find 这样做,因为在这种情况下会有多个匹配项。例如,在插入时,您可以完成 map["A"] = 1map["a"] = 2 之类的操作,现在如果您想要不区分大小写的 map.find("a"),那么预期的返回值是多少?解决此问题的最简单方法是仅在一种情况下(大写或小写)将字符串插入到 map 中,然后在查找时使用相同的情况。

    【讨论】:

    • -1,误导。不区分大小写的映射的预期值只是 2(为 A"=="a" 写入的最后一个值)。映射使用严格的弱排序,它(可以)具有等效键。任何这样的键都可以互换使用。
    • 可能会产生误导......我试图表明的是,如果你有一个区分大小写的地图,就没有办法让 find() 函数以不区分大小写的方式工作。
    • 公平点,但这一点会更清楚。例如。如果您已经解释过std::map 只支持一个索引,它可以是区分大小写的或不区分大小写的,但不能同时支持。从那里可以轻松链接到boost::multi_index,它确实支持第二个索引。
    • 如果他按照 Abhay 的建议使用不区分大小写的比较器创建地图,那么他可以使用成员版本的 find 进行不区分大小写的查找。
    • +1,因为实际上其他答案具有误导性。他们使整个地图不区分大小写,这个问题只要求使 map::find() 不区分大小写。
    【解决方案2】:

    地图模板的比较元素默认为二进制比较类“less”。看实现:

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

    您可以创建自己的派生自 binary_function 的类(less 的父类),并在不区分大小写的情况下进行相同的比较。

    【讨论】:

      【解决方案3】:

      默认情况下不会。您必须提供一个自定义比较器作为第三个参数。关注 sn-p 会帮助你...

        /************************************************************************/
        /* 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
          }
        };
      

      std::map&lt; std::string, std::vector&lt;std::string&gt;, ci_less &gt; myMap;一样使用它

      注意:std::lexicographical_compare 有一些细节。如果您考虑语言环境,字符串比较并不总是那么简单。如果有兴趣,请参阅 c.l.c++ 上的 this 线程。

      更新:在 C++11 中,std::binary_function 已被弃用并且是不必要的,因为类型是自动推断的。

        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
          }
        };
      

      【讨论】:

      • @Abhay。感谢您的回答。但是,我不完全确定这实际上是如何工作的(对 STL 来说相对较新)。如何定义第三个参数,它是一个比较函数或函数对象,执行“不区分大小写”小于,实际上是不区分大小写的比较。我们不应该使用 == 运算符。这实际上是如何工作的。我确定我错过了什么。
      • @Ankur:std::map 通常实现为某种树结构。 find() 方法使用用于对树进行排序的比较函数(既不在之前也不在之后的项目被认为是相等的——也就是严格的弱排序)。这允许使用地图的树结构在 O(logN) 时间内执行搜索。谓词对象很像一个排序函数;它的 operator() 将 MyMap::value_type& 作为参考,如果项目符合您的搜索条件,则返回 true。
      • 还有一个问题,为什么要从 binary_function 派生这些函数对象。是的,它是标准二进制函数对象的基类,但它在这里和一般解决什么目的?
      • @VictorSergienko: std::lexicographical_compare 通过它的比较器 nocase_compare 调用它
      • std::binary_function 在 C++11 中已弃用并在 C++17 中删除。
      【解决方案4】:

      您可以使用三个参数来实例化std::map:键类型、值类型和比较函数——严格的弱排序(本质上,一个函数或函子在传递性和反自反性方面表现得像operator&lt;)。只需定义第三个参数来执行“不区分大小写的小于”(例如,在它正在比较的小写字符串上通过 &lt;),您将拥有您想要的“不区分大小写的映射”!

      【讨论】:

        【解决方案5】:

        实现 std::less 函数并通过将两者更改为相同大小写进行比较。

        【讨论】:

          【解决方案6】:

          如果您不想触摸地图类型(以保持其原有的简单性和效率),但不介意使用较慢的不区分大小写的查找函数 (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:也许这是 Roger Pate 的想法,但不确定,因为有些细节有点偏离(std::search?,直接字符串比较器?)

          【讨论】:

            【解决方案7】:

            我使用以下内容:

            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);
            

            【讨论】:

            • +1 很酷,但是哇,太丑了。像我的例子一样让它成为一个仿函数并 typedef 地图。
            【解决方案8】:

            这里有一些其他的替代方案,包括一个执行速度明显更快的替代方案。

            #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's 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;
            }
            

            【讨论】:

            • '?第一种方法中的 1 : 0' 部分是愚蠢和不必要的,但其他答案很好。
            • 仅供参考,strcasecmp() 不在 中。它在 但我认为 Windows 没有它。
            • 当你说is x &lt; 0 的那一刻,你现在已经得到了一个布尔值,那么为什么要生成一个整数,然后必须将它转换为布尔值以匹配返回参数?跨度>
            • @StoneFree 对,我没注意。我想来自 C 背景的我有时会忘记有一个实际的 bool 类型这样的东西。我会编辑它。
            • 我认为当在右手边找到左手边时, strcasecmp 上的
            【解决方案9】:

            测试:

            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
            

            【讨论】:

              【解决方案10】:

              对于 C++11 及更高版本:

              #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;
              }
              

              【讨论】:

                【解决方案11】:

                我想提出一个不使用 Boost 或模板的简短解决方案。由于C++11,您还可以提供lambda expression 作为地图的自定义比较器。对于 POSIX 兼容的系统,解决方案可能如下所示:

                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);
                

                Code on Ideone

                对于 Window,strcasecmp() 不存在,但您可以使用 _stricmp() 代替:

                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);
                

                注意:根据您的系统以及是否必须支持 Unicode,您可能需要以不同的方式比较字符串。 This Q&A 是一个好的开始。

                【讨论】:

                  猜你喜欢
                  • 2021-11-12
                  • 1970-01-01
                  • 2013-10-06
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-06-22
                  • 2011-10-14
                  • 1970-01-01
                  相关资源
                  最近更新 更多